#pragma once

#include "DataValidator.h"
#include "JSON.h"
#include "Regex.h"
#include "Stack.h"
#include "Text.h"
#include "UIInitialization.h"
#include "Zeichnung.h"

namespace Framework
{
    class TextRenderer;
    class TextFeld;

    namespace JSON
    {
        enum JsonStructure
        {
            OBJECT,
            ARRAY,
            VALUE
        };

        enum SyntaxErrorType
        {
            UNEXPECTED_OBJECT_START,
            UNEXPECTED_ARRAY_START,
            UNEXPECTED_VALUE,
            UNEXPECTED_COMMA,
            UNEXPECTED_COLON,
            UNEXPECTED_END_OF_OBJECT,
            UNEXPECTED_END_OF_ARRAY,
            UNEXPECTED_START_OF_STRING,
            INVALID_VALUE,
            OTHER
        };

        struct ParserState;

        class SyntaxError
        {
        private:
            int start;
            int end;
            SyntaxErrorType errorType;

        public:
            DLLEXPORT SyntaxError(
                int start, int end, SyntaxErrorType errorType);
            DLLEXPORT SyntaxError();

            DLLEXPORT int getStart() const;
            DLLEXPORT int getEnd() const;
            DLLEXPORT SyntaxErrorType getErrorType() const;

            friend ParserState;
        };

        struct ParserState
        {
            int index;
            int keyStart;
            int keyEnd;
            int valueStart;
            int valueEnd;
            int errorStart;
            JsonStructure parent;
            bool inString;
            bool escaped;
            bool colon;
            bool key;
            bool value;
            bool inValue;
            bool valueValidationNeeded;
            char openingControllChar;
            char closingControllChar;
            bool valueExpected;

        public:
            ParserState(JsonStructure parent, bool valueExpected);

            bool next(
                const char* current, const char* next, SyntaxError& error);
        };

        class EditableJsonElement : public ReferenceCounter
        {
        private:
            EditableJsonElement* children;
            EditableJsonElement* siblings;
            EditableJsonElement* lastChild;
            EditableJsonElement* parent;
            Array<SyntaxError> errors;
            Text content;
            Text key;
            Text value;
            int valueStart;
            int keyStart;
            bool parsed;
            char openingControllChar;
            char closingControllChar;
            JsonStructure parentStructure;
            Regex::Automata<char>* valueValidator;
            bool hidden;
            int newLineCount;

        public:
            DLLEXPORT EditableJsonElement(
                Regex::Automata<char>* valueValidator);
            DLLEXPORT EditableJsonElement(
                Regex::Automata<char>* valueValidator, const char* content);
            DLLEXPORT ~EditableJsonElement();

        private:
            DLLEXPORT void setContentOnly(const char** content,
                int& startPos,
                Array<EditableJsonElement*>& afterList,
                Array<EditableJsonElement*>& deleteList);
            DLLEXPORT void setContentRecursive(const char** content,
                int& startPos,
                Array<EditableJsonElement*>& afterList,
                Array<EditableJsonElement*>& deleteList);
            DLLEXPORT void format(int indent, bool insertNewLine);
            DLLEXPORT void formatRecursive(int indent, bool insertNewLine);
            DLLEXPORT bool isValueValid(
                const char* content, int start, int end);
            DLLEXPORT void setContent(const char* content,
                int& startPos,
                Array<EditableJsonElement*>& afterList);
            DLLEXPORT void checkSyntax();

        public:
            DLLEXPORT void setContent(const char* content);
            DLLEXPORT void addChildren(EditableJsonElement* content);
            DLLEXPORT void setNextSibling(EditableJsonElement* content);
            DLLEXPORT void addSibling(EditableJsonElement* content);
            DLLEXPORT void format();
            DLLEXPORT const Text& getContent();
            DLLEXPORT Text getRecursiveContent();
            DLLEXPORT int lineCount();
            DLLEXPORT bool lineCount(bool includeChildren,
                bool includeSiblings,
                EditableJsonElement* stop,
                int& count);
            DLLEXPORT bool isHidden();
            DLLEXPORT void setHidden(bool hidden);
            DLLEXPORT EditableJsonElement* zParent();
            DLLEXPORT EditableJsonElement* zMextSibling();
            DLLEXPORT EditableJsonElement* zFirstChildren();
            DLLEXPORT EditableJsonElement* zLastChildren();
            DLLEXPORT char getOpeningControllChar();
            DLLEXPORT char getClosingControllChar();
            DLLEXPORT Text& getKey();
            DLLEXPORT Text& getValue();
            DLLEXPORT void disconnect();
            DLLEXPORT void removeChild(EditableJsonElement* zElement);
            DLLEXPORT void getWordBounds(int pos, int* left, int* right);
            DLLEXPORT bool hasError(int column) const;
            DLLEXPORT SyntaxError getError(int column) const;
            DLLEXPORT bool hasError() const;
            DLLEXPORT void makeVisible();
            DLLEXPORT bool isVisible() const;
            DLLEXPORT int getIndent() const;
            DLLEXPORT int getColor(int index);
            DLLEXPORT void removeUntil(
                int startIndex, EditableJsonElement* end, int endIndex);
            DLLEXPORT EditableJsonElement* zBefore(
                EditableJsonElement* zRoot, bool onlyVisible, int* lineCount);
            DLLEXPORT EditableJsonElement* zAfter(
                bool onlyVisible, int* lineCount);
        };

        struct EditorPosition
        {
            EditableJsonElement* line;
            int column;
        };

        enum ScrollTargetPos
        {
            Top,
            Center,
            Bottom
        };

        class JsonEditor : public ZeichnungHintergrund
        {
        private:
            Regex::Automata<char>* valueValidator;
            Validator::DataValidator* validator;
            EditableJsonElement* content;
            EditorPosition renderStart;
            int renderStartOffset;
            EditorPosition textCursor;
            EditorPosition dragStartPos;
            EditorPosition selectionStart;
            EditorPosition selectionEnd;
            EditorPosition lastClickCursorPos;
            TextRenderer* textRenderer;
            TextFeld* errorDescription;
            int renderStartLine;
            int renderStopLine;
            int lineCount;
            Punkt cursorPos;
            Punkt pressedPos;
            Punkt dragSart;
            double timeSicePress;
            bool pressed;
            double time;
            bool drawCursor;
            bool drag;
            int renderings;
            int renderedLines;
            double tps;
            bool hasSyntaxError;
            Critical cs;
            UIInit uiInit;

        public:
            DLLEXPORT JsonEditor(UIInit uiInit);
            DLLEXPORT ~JsonEditor();

        protected:
            //! Verarbeitet Maus Nachrichten
            //! \param me Das Ereignis, was durch die Mauseingabe ausgeloesst
            //! wurde
            DLLEXPORT void doMausEreignis(
                MausEreignis& me, bool userRet) override;
            DLLEXPORT EditorPosition getScreenPosition(int localX, int localY);
            DLLEXPORT void deleteSelection();
            DLLEXPORT void unifyPosition(EditorPosition& pos);
            DLLEXPORT void fixTree(EditableJsonElement* zElement);

        public:
            DLLEXPORT void setFont(Schrift* schrift);
            DLLEXPORT void setFontSize(int size);
            DLLEXPORT void setContent(Text content);
            DLLEXPORT void setContent(JSONValue* content);
            DLLEXPORT void format();
            DLLEXPORT void setValidator(Validator::DataValidator* validator);

            //! Verarbeitet ein Tastatur Ereignis. Wird vom Framework
            //! automatisch aufgerufen \param te Das Ereignis
            DLLEXPORT void doTastaturEreignis(TastaturEreignis& te) override;

            //! Updated den Zeichenhintergrund
            //! \param tickVal Die vergangene Zeit in Sekunden, die seit dem
            //! Letzten Aufruf dieser Funktion verstrichen ist \return 1, wenn
            //! das Bild neu gezeichnet werden muss. 0 sonnst
            DLLEXPORT bool tick(double tickVal) override;
            //! Zeichnet den Hintergrund eines Zeichnunges nach rObj
            DLLEXPORT void render(Bild& rObj) override;

            DLLEXPORT Text getContent();
            DLLEXPORT Text getSelectedContent();
            DLLEXPORT JSONValue* getValidContent();
            DLLEXPORT void scrollToLine(int line, ScrollTargetPos pos);
            DLLEXPORT void scrollToLine(
                EditorPosition line, ScrollTargetPos pos);
            DLLEXPORT void scrollToLine(
                int lineNum, EditorPosition target, ScrollTargetPos pos);
        };
    } // namespace JSON
} // namespace Framework