#include "TemplateFile.h"

#include "TemplateElements.h"
#include "TemplateNode.h"

#include "Lexer.h"
#include "File.h"
#include "StringUtils.h"

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

}

TemplateFile::~TemplateFile()
{

}

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

bool TemplateFile::hasLoaded() const
{
    return mHasLoaded;
}

TemplateNode* TemplateFile::getContent() const
{
    return mRootNode.get();
}

void TemplateFile::loadContent()
{
    if (mHasLoaded)
    {
        return;
    }
    mRawContent = File(mPath).readLines();

    mWorkingNode = mRootNode.get();
    mWorkingTextSpan = std::make_unique<TemplateTextBody>(mWorkingNode);
    for (const auto& line : mRawContent)
    {
        processLine(line);
    }
    onTextSpanFinished();

    mHasLoaded = true;
}

std::string TemplateFile::dumpContent()
{
    return mRootNode->getRawContent();
}

void TemplateFile::onTextSpanFinished()
{
    if (!mWorkingLineContent.empty())
    {
        mWorkingTextSpan->addLine(mWorkingLineContent);
    }

    if (mWorkingTextSpan->hasContent())
    {
        mWorkingNode->addChild(std::move(mWorkingTextSpan));
        mWorkingTextSpan = std::make_unique<TemplateTextBody>(mWorkingNode);
    }
    mWorkingLineContent.clear();
}

std::size_t TemplateFile::checkForStatement(const std::string& lineSection)
{
    if (lineSection.empty())
    {
        return 0;
    }

    std::vector<std::string> hits;
    std::size_t hit_size{0};
    if (Lexer::matchPattern("{%@%}", lineSection, '@', hits))
    {
        if (hits.size() == 1)
        {
            auto content = hits[0];

            onTextSpanFinished();

            onFoundStatement(content);
            hit_size = 4 + content.size();
        }
    }
    return hit_size;
}

std::size_t TemplateFile::checkForExpression(const std::string& lineSection)
{
    if (lineSection.empty())
    {
        return 0;
    }

    std::vector<std::string> hits;
    std::size_t hit_size{0};
    if (Lexer::matchPattern("{{@}}", lineSection, '@', hits))
    {
        if (hits.size() == 1)
        {
            auto content = hits[0];

            onTextSpanFinished();

            onFoundExpression(content);
            hit_size = 4 + content.size();
        }
    }
    return hit_size;
}

void TemplateFile::processLine(const std::string& line)
{
    std::size_t line_position = 0;
    mWorkingLineContent.clear();
    while(line_position < line.size())
    {
        const auto remaining = line.substr(line_position, line.size() - line_position);
        if(auto length = checkForStatement(remaining))
        {
            line_position += length;
        }
        else if(auto length = checkForExpression(remaining))
        {
            line_position += length;
        }
        else
        {
            mWorkingLineContent += line[line_position];
            line_position++;
        }
    }

    if (!mWorkingLineContent.empty())
    {
        mWorkingTextSpan->addLine(mWorkingLineContent);
        mWorkingLineContent.clear();
    }
}

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::onFoundExpression(const std::string& expression_string)
{
    const auto stripped = StringUtils::stripSurroundingWhitepsace(expression_string);
    auto expression = std::make_unique<TemplateExpression>(mWorkingNode, stripped);
    mWorkingNode->addChild(std::move(expression));
}

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>)
{
    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));
}
