#pragma once

#include <string>
#include <stack>
#include <memory>

class XmlDocument;
using XmlDocumentPtr = std::unique_ptr<XmlDocument>;

class XmlElement;

class XmlParser
{
public:
    enum class DocumentState
    {
        Await_Prolog,
        Build_Prolog,
        Await_Element,
        Build_Element,
        Close_Element
    };

    enum class LineState
    {
        Await_Tag_Open,
        Await_Tag_Follower,
        Await_Tag_Name,
        Await_Tag_Name_End,
        Await_Attribute_Name,
        Await_Attribute_Name_End,
        Await_Attribute_Value,
        Await_Attribute_Value_End,
        Await_Text_End
    };

public:
    XmlParser();

    void processLine(const std::string& input);

    XmlDocumentPtr getDocument();

private:
    void onLeftBracket();

    void onRightBracket();

    void onQuestionMark();

    void onForwardSlash();

    void onChar(char c);

    void onSpace(char c);

    void onAlphaNumeric(char c);

    void onNonAlphaNumeric(char c);

    void onEquals();

    void onDoubleQuote();

    void onTagOpen();

    void onTagNameStart(char c);

    void onTagNameEnd();

    void onTagClose();

    void onTextStart(char c);

    void onTextEnd();

    void onAttributeNameStart(char c);

    void onAttributeNameEnd();

    void onAttributeValueStart();

    void onAttributeValueEnd();

    void onPrologId();

    void onStartProlog();

    void onFinishProlog();

    void onElementTagEnd();

private:
    DocumentState mDocumentState;
    LineState mLineState;
    XmlDocumentPtr mDocument;
    std::stack<XmlElement*> mWorkingElements;

    std::string mWorkingAttributeName;
    std::string mWorkingTagName;
    std::string mWorkingAttributeValue;
    std::string mWorkingText;
};
