//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2008)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: parser_class.cpp
//      Краткое описание: Парсер для файлов со стандартной семантикой
//

#include <Darkstar/Kernel/Parser/parser_class.hpp>

using ker::Parser;
using ker::DFile;
using ker::Section;
using ker::Pair;

Section::Section(const std::string& name, const std::string& value)
{
	this->section_name = name;
	this->section_value = value;
};

Parser::Parser(DFile& file, bool permit_space_in_values)
{
	this->permit_space_in_value = permit_space_in_values;
	cur_string_num = 0;
	root_section.reset(new Section(ROOT_SECTION_NAME));
	root_section->section_start_line = 0;
	cur_section = root_section;
	was_success = false;
	if(!file.getIsLoaded())
	{	
		lwar << "File " << file.getFilename() << " is not loaded";
		return;
	};
	filename = file.getFilename();
	shared_array<char> file_data;
	unsigned int size = file.getSize();
	try
	{
		file_data.reset(new char[size + 1]);
		memset(file_data.get(), 0, size);
		file_data[size] = '\0';
	}
	catch(std::bad_alloc&)
	{
		lwar << "Cannot allocate memory for file data " << filename;
		return;
	};
	if(!file.read(size, file_data.get()))
	{
		lwar << "Cannot read from file: " << filename;
		return;
	};
	std::stringstream stream(file_data.get());
	std::string next_line;
	unsigned int open_bracket_counter = 0;
	unsigned int close_bracket_counter = 0;
	unsigned int string_number = 1;
	while(std::getline(stream, next_line))
	{
		if(_processString(next_line))
		{
			FileString new_fs;
			new_fs.str = next_line;
			new_fs.number = string_number;
			new_fs.type = UNKNOWN;
			this->file.push_back(new_fs);
		};
		++string_number;
	};
	STRING_TYPE type;
	for(unsigned int i = 0; i < (unsigned int)(this->file.size()); ++i)
	{
		if(!_getStringType(i))
		{
			lwar << "Cannot parse file: " << filename;
			return;
		};
		type = this->file[i].type;
		if(type == OPEN_BRACKET)
			++open_bracket_counter;
		if(type == CLOSE_BRACKET)
			++close_bracket_counter;
	};
	if(open_bracket_counter != close_bracket_counter)
	{
		lwar << "Number of open brackets is not equal to number of close bracket in file " << filename;
		return;
	};
	if(!_parseNextBlock())
	{
		lwar << "Cannot parse file " << filename;
		return;
	};
	was_success = true;
};

Parser::~Parser()
{

};

bool Parser::getIsParsedSuccessfully() const
{
	return was_success;
};

bool _getIsBracket(const char bracket_type, const std::string& str)
{
	if(str.length() == 1 && str[0] == bracket_type)
		return true;
	return false;
};

bool Parser::_getStringType(unsigned int number)
{
	FileString& fs = file[number];
	if(fs.type != UNKNOWN)
		return true;
	unsigned int equal_pos = (unsigned int)fs.str.find('=');
	if(equal_pos != std::string::npos)
	{
		if(number == (unsigned int)file.size())
		{
			lwar << "Error in line " << fs.number << " must be close bracket";
			return false;
		};
		if(number != (unsigned int)file.size() - 1)
		{
			FileString& next_fs = file[number + 1];
			if(_getIsBracket('{', next_fs.str))
			{
				next_fs.type = OPEN_BRACKET;
				fs.type = SECTION_NAME_VALUE;
			}
			else
				fs.type = PAIR_NAME_VALUE;
		}
		else
			fs.type = PAIR_NAME_VALUE;
		return true;
	};
	if(_getIsBracket('{', fs.str))
	{
		fs.type = OPEN_BRACKET;
		return true;
	};
	if(_getIsBracket('}', fs.str))
	{
		fs.type = CLOSE_BRACKET;
		return true;
	};
	fs.type = SECTION_NAME;
	return true;
};

bool Parser::_processString(std::string& str) const
{
	if(str.length() == 0)
		return false;
	unsigned int begin_pos = (unsigned int)str.find_first_not_of(" \a\b\f\n\r\t\v");
	if(begin_pos == std::string::npos)
		return false;
	unsigned int end_pos = (unsigned int)str.find_last_not_of(" \a\b\f\n\r\t\v");
	str = str.substr(begin_pos, end_pos - begin_pos + 1);
	if(str[0] == '#')
		return false;
	return true;
};

bool Parser::_parseStringToPair(const std::string& str, Pair& pair, bool value_can_be_empty) const
{
	std::string name = "";
	std::string value = "";
	unsigned int cur_pos = 0;
	unsigned int str_length = (unsigned int)str.length();
	// fill name
	while(cur_pos != str_length)
	{
		if(str[cur_pos] == '=' || isspace((int)str[cur_pos]))
			break;
		++cur_pos;
	};
	name = str.substr(0, cur_pos);
	bool is_equal_found = false;
	while(cur_pos != str_length)
	{
		if(str[cur_pos] == '=')
		{
			is_equal_found = true;
			++cur_pos;
			continue;
		};
		if(!isspace((int)str[cur_pos]))
		{
			if(is_equal_found)
				break;
			else
				return false;
		};
		++cur_pos;
	};
	if(cur_pos == str_length && is_equal_found)
		return false;
	unsigned int start_pos = cur_pos;
	while(cur_pos != str_length)
	{
		if(!permit_space_in_value)
			if(isspace((int)str[cur_pos]))
				return false;
		++cur_pos;
	};
	value = str.substr(start_pos);
	if(value == "" && !value_can_be_empty)
		return false;
	pair.name = name;
	pair.value = value;
	return true;
};

bool Parser::_parseNextBlock()
{
	if(cur_string_num != 0)
	{
		FileString fs = file[cur_string_num];
		if(fs.type != OPEN_BRACKET)
		{
			lwar << "Each section must be surround by bracket. File: " << filename << " line: " << fs.number;
			return false;
		};
		++cur_string_num;
	};
	bool is_bracket_close = false;
	shared_ptr<Section> this_section = cur_section;
	this_section->section_start_line = cur_string_num-1;
	while(cur_string_num < (unsigned int)file.size())
	{
		FileString cur_fs = file[cur_string_num];
		switch(cur_fs.type)
		{
		case CLOSE_BRACKET:
			{
				++cur_string_num;
				return true;
			};break;
		case OPEN_BRACKET:
			{
				lwar << "Error in file " << filename << " line: " << cur_fs.number << " multiple open bracket";
				return false;
			};break;
		case PAIR_NAME_VALUE:
			{
				Pair new_pair;
				if(!_parseStringToPair(cur_fs.str, new_pair))
				{
					lwar << "Cannot parse string number " << cur_fs.number << " in file " << filename;
					return false;
				};
				if(this_section->values.find(new_pair.name) != this_section->values.end())
				{
					lwar << "In one section cannot be two equal names. File " << filename << " line " << cur_fs.number << " name " << new_pair.name;
					return false;
				};
				PairInfo new_info;
				new_info.value = new_pair.value;
				new_info.line_in_file = cur_fs.number;
				this_section->values.insert(std::pair<std::string, PairInfo>(new_pair.name, new_info));
			};break;
		case SECTION_NAME:
		case SECTION_NAME_VALUE:
			{
				Pair new_pair;
				if(!_parseStringToPair(cur_fs.str, new_pair, true))
				{
					lwar << "Not correct string in file " << filename << " line " << cur_fs.number;
					return false;
				};
				shared_ptr<Section> new_section(new Section(new_pair.name, new_pair.value));
				this_section->sections.push_back(new_section);
				cur_section = new_section;
				++cur_string_num;
				if(!_parseNextBlock())
					return false;
				continue;
			};break;
		};
		++cur_string_num;
	};
	return true;
};

const shared_ptr<Section>& Parser::getRootSection() const
{
	return root_section;
};