#include "LatexMathExpression.h"

#include "FileLogger.h"

LatexMathExpression::LatexMathExpression(const std::string& expression)
    : mRawBody(expression),
        mType(Type::LINEAR)
{
    MLOG_INFO("Adding expression with content: " << expression);
    parse();
}

LatexMathExpression::LatexMathExpression(Type type)
    : mType(type)
{

}

LatexMathExpression::~LatexMathExpression()
{

}

const LatexMathSymbol& LatexMathExpression::getLeftSymbol() const
{
    return mLeftSymbol;
}

const LatexMathSymbol& LatexMathExpression::getRightSymbol() const
{
    return mRightSymbol;
}

void LatexMathExpression::setLeftSymbol(const LatexMathSymbol& symbol)
{
    mLeftSymbol = symbol;
}

void LatexMathExpression::setRightSymbol(const LatexMathSymbol& symbol)
{
    mRightSymbol = symbol;
}

void LatexMathExpression::onTagBodyChar(char c)
{
    if (c == '}')
    {
        onRBrace();
    }
    else
    {
        mWorkingString += c;
    }
}

void LatexMathExpression::onFreeChar(char c)
{
    if (c == '\\')
    {
        mLineState = LineState::IN_TAG_NAME;
    }
    else if (c == '{')
    {
        onLBrace();
    }
    else if (c == '}')
    {
        onRBrace();
    }
    else if (c == '^')
    {
        onSuperscript();
    }
    else if (c == '_')
    {
        onSubscript();
    }
    else if (std::isblank(c))
    {
        onSpace();
    }
    else
    {
        mWorkingString += c;
    }
}

void LatexMathExpression::parse()
{
    mLineState = LineState::NONE;
    mWorkingString.clear();

    for (auto c : mRawBody)
    {
        if (mLineState == LineState::IN_TAG_BODY)
        {
            onTagBodyChar(c);
        }
        else
        {
            onFreeChar(c);
        }
    }
    onSpace();
    onCloseExpression();
}

void LatexMathExpression::onSpace()
{
    if (mLineState == LineState::IN_TAG_NAME)
    {
        onFinishedTagName();
    }
    else
    {
        onLiteral();
    }
    mWorkingString.clear();
}

void LatexMathExpression::onLBrace()
{
    if (mLineState == LineState::IN_TAG_NAME)
    {
        onFinishedTagName();
    }
    else if (mLineState == LineState::AWAITING_LBRACE)
    {
        mLineState = LineState::IN_TAG_BODY;
    }
    else if (mLineState == LineState::IN_TAG_BODY)
    {
        mOpenBraceCount++;
    }
    else
    {
        mWorkingString += "{";
    }
}

void LatexMathExpression::onRBrace()
{
    if (mLineState == LineState::IN_TAG_BODY)
    {
        if (mOpenBraceCount == 0)
        {
            onTagBody();
        }
        else
        {
            mOpenBraceCount--;
            mWorkingString += "}";
        }
    }
    else
    {
        mWorkingString += "}";
    }
}

void LatexMathExpression::setRawContent(const std::string& content)
{
    auto expression = std::make_unique<LatexMathExpression>(content);
    mExpressions.push_back(std::move(expression));
}

void LatexMathExpression::onTagBody()
{
    onCloseExpression();
}

void LatexMathExpression::onSuperscript()
{

}

void LatexMathExpression::onSubscript()
{

}

void LatexMathExpression::onLiteral()
{
    LatexMathSymbol symbol{ LatexMathSymbol::Type::RAW, mWorkingString, mWorkingString };
    mWorkingSymbols.push_back(symbol);
}

void LatexMathExpression::onFinishedTagName()
{
    if (auto lookup = LatexSymbolLookup::getKnownTag(mWorkingString); lookup)
    {
        if (mLineState == LineState::AWAITING_LBRACE || mLineState == LineState::IN_TAG_NAME)
        {
            onCloseExpression();

            if (*lookup == LatexMathSymbol::Tag::FRAC)
            {
                onFracTag();
                mWorkingString.clear();
            }
            else if (auto tag_type = LatexSymbolLookup::getTagType(*lookup); tag_type == LatexMathSymbol::Type::ENCLOSING_TAG)
            {
                onEnclosingTag(*lookup);
            }
            mLineState = LineState::IN_TAG_BODY;
        }
    }
    else if (auto lookup = LatexSymbolLookup::getSymbolUtf8(mWorkingString); lookup)
    {
        LatexMathSymbol symbol{ LatexMathSymbol::Type::TAG, mWorkingString, *lookup };
        mWorkingSymbols.push_back(symbol);
        mLineState = LineState::NONE;
    }
}

void LatexMathExpression::onFracTag()
{
    mWorkingType = Type::FRACTION;
    auto expression = std::make_unique<LatexMathExpression>(mWorkingType);
    mWorkingExpression = expression.get();
    mExpressions.push_back(std::move(expression));
}

void LatexMathExpression::onEnclosingTag(LatexMathSymbol::Tag tag)
{
    mWorkingType = Type::ENCLOSING;
    auto expression = std::make_unique<LatexMathExpression>(mWorkingType);

    auto left_symbol_value = LatexSymbolLookup::getLeftSymbolUtf8(tag);
    auto right_symbol_value = LatexSymbolLookup::getRightSymbolUtf8(tag);

    MLOG_INFO("Adding enclosing tag");

    expression->setLeftSymbol({ LatexMathSymbol::Type::ENCLOSING_TAG, "left", *left_symbol_value });
    expression->setRightSymbol({ LatexMathSymbol::Type::ENCLOSING_TAG, "right", *right_symbol_value });

    mWorkingExpression = expression.get();
    mExpressions.push_back(std::move(expression));
}

void LatexMathExpression::onCloseExpression()
{
    if (mWorkingType == Type::LEAF)
    {
        MLOG_INFO("onCloseExpression LEAF");
        if (!mWorkingSymbols.empty())
        {
            MLOG_INFO("Has working symbols");
            auto expression = std::make_unique<LatexMathExpression>(mWorkingType);
            expression->setContent(mWorkingSymbols);
            mExpressions.push_back(std::move(expression));
        }
    }
    else if (mWorkingType == Type::FRACTION)
    {
        MLOG_INFO("onCloseExpression FRACTION");
        mWorkingExpression->setRawContent(mWorkingString);

        if (mWorkingExpression->getExpressions().size() == 2)
        {
            mWorkingType = Type::LEAF;
            mLineState = LineState::NONE;
        }
        else
        {
            mLineState = LineState::AWAITING_LBRACE;
            mOpenBraceCount = 0;
        }
    }
    else if (mWorkingType == Type::ENCLOSING)
    {
        MLOG_INFO("onCloseExpression ENCLOSING");
        if (!mWorkingString.empty())
        {
            mWorkingExpression->setRawContent(mWorkingString);

            if (mWorkingExpression->getExpressions().size() == 1)
            {
                mWorkingType = Type::LEAF;
                mLineState = LineState::NONE;
            }
        }
    }

    mWorkingString.clear();
    mWorkingSymbols.clear();
}

void LatexMathExpression::setContent(std::vector<LatexMathSymbol>& symbols)
{
    mSymbols = symbols;
}

const std::vector<LatexMathSymbol>& LatexMathExpression::getSymbols() const
{
    return mSymbols;
}

LatexMathExpression::Type LatexMathExpression::getType() const
{
    return mType;
}

const std::vector<LatexMathExpressionPtr>& LatexMathExpression::getExpressions() const
{
    return mExpressions;
}
