#pragma once

#include "LatexSymbols.h"

#include <string>
#include <memory>
#include <vector>

class LatexMathExpression;
using LatexMathExpressionPtr = std::unique_ptr<LatexMathExpression>;

class LatexMathExpression
{
public:
    enum class Type
    {
        LINEAR,
        LEAF,
        FRACTION,
        ENCLOSING,
        SUBSCRIPT,
        SUPERSCRIPT
    };

    enum class LineState
    {
        NONE,
        IN_TAG_NAME,
        AWAITING_LBRACE,
        IN_TAG_BODY
    };

    LatexMathExpression(const std::string& expression);

    LatexMathExpression(Type type);

    ~LatexMathExpression();

    const std::vector<LatexMathSymbol>& getSymbols() const;

    const std::vector<LatexMathExpressionPtr>& getExpressions() const;

    const LatexMathSymbol& getLeftSymbol() const;

    const LatexMathSymbol& getRightSymbol() const;

    Type getType() const;

    void setLeftSymbol(const LatexMathSymbol& symbol);

    void setRightSymbol(const LatexMathSymbol& symbol);

    void setContent(std::vector<LatexMathSymbol>& symbols);

    void setRawContent(const std::string& content);

    void onTagBodyChar(char c);

    void onFreeChar(char c);

    void onSpace();

    void onLBrace();

    void onRBrace();

    void onSuperscript();

    void onSubscript();

    void onLiteral();

    void onTagBody();

    void onFinishedTagName();

    void onCloseExpression();

    void onFracTag();

    void onEnclosingTag(LatexMathSymbol::Tag tag);

    void parse();

private:
    std::size_t mOpenBraceCount{ 0 };

    LineState mLineState{ LineState::NONE };
    std::string mWorkingString;
    std::string mRawBody;

    Type mWorkingType{ Type::LEAF };
    LatexMathExpression* mWorkingExpression{ nullptr };
    Type mType{ Type::LEAF };
    std::vector<LatexMathSymbol> mWorkingSymbols;

    LatexMathSymbol mLeftSymbol;
    LatexMathSymbol mRightSymbol;

    std::vector<LatexMathSymbol> mSymbols;
    std::vector<LatexMathExpressionPtr> mExpressions;
};
