#include "TemplateFile.h"

#include <iostream>

TemplateFile::TemplateFile(const Path& path)
	: mPath(path),
	mRootNode(std::make_unique<TemplateNode>(nullptr))
{

}

std::vector<std::string> TemplateFile::getProcessedContent() const
{
	return mProcessedContent;
}

std::string TemplateFile::getName() const
{
	return mPath.stem().string();
}

void TemplateFile::loadContent()
{
	std::cout << "Trying to load file at: " << mPath << std::endl;
	mRawContent = File(mPath).readLines();
	mWorkingLine = 0;
	mWorkingNode = mRootNode.get();
	mWorkingTextBody = std::make_unique<TemplateTextBody>(mWorkingNode);
	for (const auto& line : mRawContent)
	{
		processLine(line);
		mWorkingLine++;
	}
	onTextBodyFinished();
}

void TemplateFile::onTextBodyFinished(std::string working_string)
{
	if (!working_string.empty())
	{
		mWorkingTextBody->addLine(working_string);
	}

	if (mWorkingTextBody->hasContent())
	{
		mWorkingNode->addChild(std::move(mWorkingTextBody));
		mWorkingTextBody = std::make_unique<TemplateTextBody>(mWorkingNode);
	}
}

void TemplateFile::processLine(const std::string& line)
{
	bool last_was_ldelimiter{ false };
	bool last_was_statement_rdelimiter{ false };
	bool last_was_expression_rdelimiter{ false };
	bool in_statement{ false };
	bool in_expression{ false };
	std::string working_string;
	std::string last_working_string;
	for (auto c : line)
	{
		if (c == '{')
		{
			if (last_was_ldelimiter)
			{
				in_expression = true;
				last_was_ldelimiter = false;
				working_string = "";
			}
			else
			{
				last_was_ldelimiter = true;
			}
		}
		else if (c == '%' && last_was_ldelimiter)
		{
			last_was_ldelimiter = false;
			in_statement = true;
			last_working_string = working_string;
			working_string = "";
		}
		else if (c == '%' && in_statement)
		{
			last_was_statement_rdelimiter = true;
		}
		else if (c == '}' && (last_was_statement_rdelimiter || in_expression || last_was_expression_rdelimiter))
		{
			if (last_was_statement_rdelimiter)
			{
				onTextBodyFinished(last_working_string);
				onFoundStatement(working_string);
				last_was_statement_rdelimiter = false;
				working_string = "";
			}
			else if (in_expression)
			{
				last_was_expression_rdelimiter = true;
				in_expression = false;
			}
			else
			{
				onTextBodyFinished();
				onFoundExpression(working_string);
				last_was_expression_rdelimiter = false;
				working_string = "";
			}
		}
		else if (last_was_ldelimiter && (!in_statement && !in_expression))
		{
			last_was_ldelimiter = false;
			working_string += '{';
			working_string += c;
		}
		else
		{
			working_string += c;
		}
	}

	if (!working_string.empty())
	{
		mWorkingTextBody->addLine(working_string);
	}
}

void TemplateFile::onFoundStatement(const std::string& statement_string)
{
	const auto statement_elements = StringUtils::split(statement_string);

	if (statement_elements.size() == 0)
	{
		return;
	}
	const auto statement_type = statement_elements[0];
	if (statement_type == "block" && statement_elements.size() == 2)
	{
		onFoundBlock(statement_elements);
	}
	else if (statement_type == "endblock")
	{
		onFoundEndBlock(statement_elements);
	}
	else if (statement_type == "extends")
	{
		onFoundExtends(statement_elements);
	}

}

void TemplateFile::onFoundBlock(const std::vector<std::string> args)
{
	if (args.size() != 2)
	{
		return;
	}
	auto block = std::make_unique<TemplateBlock>(mWorkingNode, args[1]);
	auto temp = block.get();
	mWorkingNode->addChild(std::move(block));
	mWorkingNode = temp;
}

void TemplateFile::onFoundEndBlock(const std::vector<std::string> args)
{
	mWorkingNode = mWorkingNode->getParent();
}

void TemplateFile::onFoundExtends(const std::vector<std::string> args)
{
	if (args.size() != 2)
	{
		return;
	}
	auto extends = std::make_unique<TemplateExtends>(mWorkingNode, args[1]);
	mWorkingNode->addChild(std::move(extends));
}

void TemplateFile::onFoundExpression(const std::string& expression_string)
{
	auto expression = std::make_unique<TemplateExpression>(mWorkingNode, expression_string);
	mWorkingNode->addChild(std::move(expression));
}

void TemplateFile::dumpContent()
{
	auto content = mRootNode->getRawContent();
	std::cout << content << std::endl;
}