#include "MacroManager.h"

#define METHOD_NAME "UscFileParser::" << UPP_FUNCTION_NAME << "(): "

namespace Upp {

UscFileParser::UscFileParser(const String& filePath)
	: filePath(filePath)
{}

UscFileParser::MacroList UscFileParser::Parse()
{
	MacroList ret;
	
	String fileContent = LoadFile(filePath);
	if(fileContent.IsEmpty()) {
		Logw() << METHOD_NAME << "Following file \"" << filePath << "\" doesn't exist or is empty.";
		return ret;
	}

	try {
		CParser parser(fileContent, filePath);
		
		while (!parser.IsEof()) {
			String comment = TrimLeft(String(parser.GetSpacePtr(), parser.GetPtr()));

			if (!parser.IsId())
				return ret;
			
			const char* prototypeBegin = parser.GetPtr();
			String id = parser.ReadId();
			
			if(id.IsEqual("fn"))
				ReadFunction(parser, comment, prototypeBegin, ret);
			else
			if(id.IsEqual("macro"))
				ReadMacro(parser, comment, prototypeBegin, ret);
			else
				return ret;
		}
	}
	catch (const CParser::Error& error) {
		Logw() << METHOD_NAME << "Parsing file \"" << filePath << "\" failed with error: " << error << ".";
	}
	
	return ret;
}

bool UscFileParser::IsValid()
{
	return !Parse().IsEmpty();
}

void UscFileParser::ReadFunction(CParser& parser, const String& comment, const char* prototypeBegin, MacroList& list)
{
	String fileName = parser.GetFileName();
	MacroElement fn(MacroElement::Type::FUNCTION, fileName, parser.GetLine(), comment);
	
	if(!parser.IsId()) {
		FindNextElement(parser);
		return;
	}
	fn.name = parser.ReadId();
	if(!parser.Char('(')) {
		FindNextElement(parser);
		return;
	}
	fn.args = ReadArgs(parser);
	
	FinishRead(parser, prototypeBegin, fn, list);
}

void UscFileParser::ReadMacro(CParser& parser, const String& comment, const char* prototypeBegin, MacroList& list)
{
	String fileName = parser.GetFileName();
	MacroElement macro(MacroElement::Type::MACRO, fileName, parser.GetLine(), comment);
	
	if(!parser.IsString()) {
		FindNextElement(parser);
		return;
	}
	macro.name = String() << (parser.IsString() ? parser.ReadString() : String());
	if(parser.Char(':')) {
		if(!parser.IsString()) {
			FindNextElement(parser);
			return;
		}

		macro.name << " : " << (parser.IsString() ? parser.ReadString() : String());
	}
	if (!parser.IsChar('{'))
		ReadKeyDesc(parser);

	FinishRead(parser, prototypeBegin, macro, list);
}

void UscFileParser::FinishRead(CParser& parser, const char* prototypeBegin, MacroElement& element, MacroList& list)
{
	const char* bodyBegin = parser.GetPtr();

	element.prototype = String(prototypeBegin, bodyBegin);
	
	if (!parser.Char('{')) {
		FindNextElement(parser);
		return;
	}

	Upp::SkipBlock(parser);

	if(parser.GetSpacePtr() > bodyBegin)
		element.code = String(bodyBegin, parser.GetSpacePtr());

	list.Add(element);
}

void UscFileParser::FindNextElement(CParser& parser)
{
	while(!parser.IsEof() && !parser.IsId("fn") && !parser.IsId("macro"))
		parser.SkipTerm();
}

String UscFileParser::ReadArgs(CParser& parser)
{
	int level = 1;
	parser.Char('(');
	
	String ret = "(";
	while(level > 0 && !parser.IsEof()) {
		if (parser.Char('('))
			level++;
		else
		if (parser.Char(')'))
			level--;
		else
			ret << parser.GetChar();
	}
	ret << ")";

	return ret;
}

String UscFileParser::ReadKeyDesc(CParser& parser)
{
	if(!parser.IsId())
		return String();

	String ret = parser.ReadId();

	while(!parser.IsEof() && parser.Char('+')) {
		if(parser.IsId())
			ret << "+" << parser.ReadId();

		else
		if(parser.IsNumber())
			ret << "+" << parser.ReadInt();
		else
			break;
	}

	return ret;
}

}
