#pragma once

#include "Array.h"
#include "Drawing.h"

namespace Framework
{
    class Font;    //! Font.h
    class Text;       //! Text.h
    class AlphaField;  //! AlphaField.h
    class Border;     //! Border.h
    class TextField;   //! from this file
    class VScrollBar; //! Scroll.h
    class HScrollBar; //! Scroll.h
    class TextRenderer;

    struct TextStyle
    {
        int beginIndex;
        unsigned char fontSize;
        int fontColor;
        int selectedColor;
        int selectedBackcroundColor;
        bool underlined;
        bool selected;
        int interactParam;
        unsigned char rendererIndex;
        DLLEXPORT bool equals(const TextStyle& rhs);
    };

    //! Manages a text field
    class TextField : public DrawableBackground
    {
    public:
        class TextStyleManager : public virtual ReferenceCounter
        {
        protected:
            RCArray<TextRenderer>* renderer;
            Array<TextStyle> textStyle;
            int index;
            int styleIndex;
            TextStyle current;
            Text* text;

        public:
            DLLEXPORT TextStyleManager();
            DLLEXPORT virtual ~TextStyleManager();
            //! Sets the style of a text section
            //! \param begin The start position of the section
            //! \param end The end position of the section (not included)
            DLLEXPORT void setTextStyle(int begin, int end, TextStyle style);
            //! Removes a text section
            //! \param begin The index of the first affected character
            //! \param end The index of the first character after the section
            DLLEXPORT void removeText(int begin, int end);
            //! Inserts text at a specific position
            //! \param pos The position of the new text section
            //! \param text The new text
            DLLEXPORT void insertText(int pos, const char* text);
            //! Removes unneeded identical styles
            DLLEXPORT void cleanupStyles();
            //! Returns a reference to the style object
            DLLEXPORT TextStyle& currentStyle();
            //! Returns the current text renderer
            DLLEXPORT TextRenderer* zCurrentRenderer();
            //! Changes the content of the style object to the style of the
            //! next character. Returns 0 if there is no next character
            DLLEXPORT bool nextStyle();
            //! Changes the content of the style object to the style of the
            //! specified character \param index The index of the character
            //! to jump to. Returns 0 if the character does not exist
            DLLEXPORT bool stepTo(int index);
            //! Changes the content of the style object to the style of the
            //! first character
            DLLEXPORT void resetIteration();
            //! Returns the style of a specific character
            //! \param index The index of the character
            DLLEXPORT TextStyle getTextStyle(int index) const;
            friend TextField;
        };

        class Style : public DrawableBackground::Style
        {
        public:
            //! If this flag is not set, all line breaks are automatically
            //! removed from the text
            static const __int64 Multiline = 0x001000;
            //! If this flag is set, the text is placed exactly in the
            //! horizontal center of the field
            static const __int64 HCenter = 0x002000;
            //! If this flag is set, the text is placed exactly in the
            //! vertical center of the field
            static const __int64 VCenter = 0x004000;
            //! If this flag is set, the text can be edited by the user
            static const __int64 Editable = 0x2000000;
            //! Automatically inserts line breaks during rendering so that the
            //! width of the text field is not exceeded. The text itself is not
            //! modified. Requires Style Mehrzeilig
            static const __int64 AutoLineBreak = 0x4000000;

            //! Combines the flags HCenter and VCenter
            static const __int64 Center = HCenter | VCenter;
            //! Combines the flags Visible, Allowed, Border,
            //! Buffered, VCenter
            static const __int64 TextField
                = Visible | Allowed | Border | Buffered | VCenter | Editable;
            //! Combines the flags Visible,
            //! Mehrzeilig
            static const __int64 Text = Visible | Multiline | Allowed;
            //! Combines the flags Visible, Allowed, Border,
            //! Background, Mehrzeilig, VScroll
            static const __int64 TextGebiet = Visible | Allowed | Border
                                            | Background | Editable
                                            | Multiline | VScroll;
            //! Combines the flags VScroll and HScroll
            static const __int64 Scroll = VScroll | HScroll;
        };

    private:
        TextStyleManager* tm;
        Text* autoLineBreakSpacing;
        unsigned char showChar;
        int cpos;
        double tickVal;
        bool mouseKlick;
        std::function<void(int, int, MouseEvent me)> charEvent;

        int getTextHeight() const;
        int getTextWidth() const;
        //! Processes mouse messages
        //! \param me The event triggered by the mouse input
        DLLEXPORT virtual void doMouseEvent(
            MouseEvent& me, bool userRet) override;

    public:
        //! Constructor
        DLLEXPORT TextField();
        //! Destructor
        DLLEXPORT virtual ~TextField();
        //! charEvent: a function called when the mouse is on a specific
        //! character and the interactParam in the style is != 0
        //! \param charEvent charEvent( charIndex, interactParam, mouseEreignis );
        DLLEXPORT void setCharEvent(
            std::function<void(int, int, MouseEvent me)> charEvent);
        //! Sets a pointer to the text in the text field
        //! \param txt The pointer to the text
        DLLEXPORT void setTextZ(Text* txt);
        //! Sets the text of the text field
        //! \param txt The text
        DLLEXPORT void setText(Text* txt);
        //! Sets the text of the text field
        //! \param txt The text
        DLLEXPORT void setText(const char* txt);
        // Sets the text with styles
        //  txt: the text
        //  format: \x1: enables underline
        //          \x2FF: sets the font size for the following text.
        //              FF is a two-digit hex value
        //          \x3AARRGGBB: sets the font color.
        //              AARRGGBB is an 8-digit hex value with two characters
        //              each for alpha, red, green and blue
        //          \x4AARRGGBB: sets the color of the selected text.
        //              AARRGGBB is an 8-digit hex value with two characters
        //              each for alpha, red, green and blue
        //          \x5AARRGGBB: sets the background color of the selected text.
        //              AARRGGBB is an 8-digit hex value with two characters
        //              each for alpha, red, green and blue
        //          \x6FF: sets text renderer index.
        //              FF is a two-digit hex value
        //          \x7: disables underline
        //          \x8FFFFFFFF: set interact param.
        //              FFFFFFFF is an 8-digit hex value
        DLLEXPORT void setFormattedText(const char* txt);
        //! Inserts line breaks so that the text does not exceed the width
        //! of the text field \param spacing A text inserted directly after
        //! each inserted line break
        DLLEXPORT void addLineBreaks(const char* spacing = "");
        //! Inserts line breaks into the text txt so that the text does not
        //! exceed the width of the text field \param txt The text to insert
        //! line breaks into \param spacing A text inserted directly after
        //! each inserted line break \return The text with line breaks
        DLLEXPORT Text addLineBreaksToText(const char* txt,
            const char* spacing = "",
            bool includeFormat = 1) const;
        //! Sets a character sequence that is inserted after each line break
        //! added with the AutoLineBreak style
        DLLEXPORT void setAutoLineBreakSpacing(const char* spacing);
        //! Sets the style of a text section
        //! \param begin The start position of the section
        //! \param end The end position of the section (not included)
        DLLEXPORT void setTextStyle(int begin, int end, TextStyle style);
        //! Appends a line to the text
        //! \param zeile The new line
        DLLEXPORT void addRow(const char* zeile);
        //! Appends a line to the text
        //! \param zeile The new line
        //! \param color The color of the line
        DLLEXPORT void addRow(const char* zeile, int color);
        //! Deselects all text sections
        DLLEXPORT void deselectSelahl();
        //! Sets the selected text section
        //!  pos1: The cursor position in the text
        //!  pos2: The position in the text up to which the text should be
        //!  colored
        DLLEXPORT void setSelection(int pos1, int pos2);
        //! Sets the selected text section
        //! \param selection A point with x as cursor position and y as the
        //! position up to which the text should be colored
        DLLEXPORT void setSelection(Point& selection);
        //! Sets the selected text section
        //!  pos1: The cursor position in the text
        //!  pos2: The position in the text up to which the text should be
        //!  colored
        DLLEXPORT void addSelahl(int pos1, int pos2);
        //! Sets the selected text section
        //! \param selection A point with x as cursor position and y as the
        //! position up to which the text should be colored
        DLLEXPORT void addSelahl(Point& selection);
        //! Sets the selected text section
        //! \param begin The cursor position in the text
        //! \param end The position in the text up to which the text should be
        //! colored
        DLLEXPORT void invertSelection(int begin, int end);
        //! Replaces all selected text sections with a text
        //! \param text The new text
        DLLEXPORT void replaceSelection(const char* text);
        //! Sets the TextRenderer to use
        //! \param textRd The text renderer
        DLLEXPORT void setTextRendererZ(TextRenderer* textRd);
        //! Adds a TextRenderer
        //! \param textRd The text renderer
        DLLEXPORT void addTextRendererZ(TextRenderer* textRd);
        //! Sets the TextRenderers to use
        //! \param textRd The text renderers
        DLLEXPORT void setTextRendererZ(RCArray<TextRenderer>* textRd);
        //! Sets a pointer to the font
        //! \param font The font to use for text drawing.
        DLLEXPORT void setFontZ(Font* font);
        //! Sets a pointer to the font
        //! \param rendererIndex The index of the renderer whose font should
        //! be set \param font The font to use for text drawing.
        DLLEXPORT void setFontZ(int rendererIndex, Font* font);
        //! Sets the font size (default: 12)
        //! \param gr The font size to use for text drawing
        DLLEXPORT void setFontSize(unsigned char gr);
        //! Sets the font size (default: 12)
        //! \param begin The index of the first affected character
        //! \param end The index of the first unaffected character
        //! \param gr The font size to use for text drawing
        DLLEXPORT void setFontSize(int begin, int end, unsigned char gr);
        //! Sets the font color
        //! \param fc The color to use for text drawing
        DLLEXPORT void setFontColor(int fc);
        //! Sets the font color
        //! \param begin The index of the first affected character
        //! \param end The index of the first unaffected character
        //! \param fc The color to use for text drawing
        DLLEXPORT void setFontColor(int begin, int end, int fc);
        //! Sets a character to use for drawing, regardless of the text
        //! field's text (requires drawing flag: Border)
        //! \param c The character to draw
        //! Example: setShowChar( '*' ); for password text fields
        DLLEXPORT void setSchowChar(unsigned char c);
        //! Scrolls to a specific line (requires drawing flag: VScroll)
        //! \param zeile The index of the line that should be the top visible line
        DLLEXPORT void setVScrollToRow(int zeile);
        //! Scrolls to a specific position in the text. Without the Allowed
        //! flag, always scrolls to the bottom. (requires drawing flag:
        //! VScroll) \param pos The index of the character to scroll to.
        //! By default scrolls to the cursor position
        DLLEXPORT void updateVScroll(int pos = -1);
        //! Scrolls to a specific position in the text. Requires the Allowed
        //! flag. (requires drawing flag: HScroll) \param pos The index of
        //! the character to scroll to. By default scrolls to the cursor position
        DLLEXPORT void updateHScroll(int pos = -1);
        //! Returns the width in pixels needed to fully display the current
        //! text with the current styles
        DLLEXPORT int getNeededWidth();
        //! Returns the height in pixels needed to fully display the current
        //! text with the current styles
        DLLEXPORT int getNeededHeight();
        //! Updates the object. Called by the framework
        //! \param tickVal The time in seconds since the last call of this
        //! function \return 1 if something changed and the image needs to be
        //! redrawn. 0 otherwise
        DLLEXPORT virtual bool tick(double tickval) override;
        //! Processes keyboard messages
        //! \param me The event triggered by the keyboard input
        DLLEXPORT void doKeyboardEvent(KeyboardEvent& te) override;
        //! Draws the object to zRObj if it is visible
        //! \param zRObj The image to draw into
        DLLEXPORT virtual void render(Image& zRObj) override;
        //! Returns the text from the text field
        DLLEXPORT Text* getText() const;
        //! Returns the text from the text field without increased reference
        //! counter
        DLLEXPORT Text* zText() const;
        //! Returns the font.
        //! \return 0 if the font was not set
        DLLEXPORT Font* getFont() const;
        //! Returns the font without increased reference counter
        //! \return 0 if the font was not set
        DLLEXPORT Font* zFont() const;
        //! Returns the font.
        //! \param rendererIndex The index of the renderer whose font should
        //! be returned \return 0 if the font was not set
        DLLEXPORT Font* getFont(int rendererIndex) const;
        //! Returns the font without increased reference counter
        //! \param rendererIndex The index of the renderer whose font should
        //! be returned \return 0 if the font was not set
        DLLEXPORT Font* zFont(int rendererIndex) const;
        //! Returns the TextRenderer.
        //! \return 0 if the TextRenderer was not set
        DLLEXPORT TextRenderer* getTextRenderer() const;
        //! Returns the TextRenderer without increased reference counter
        //! \return 0 if the TextRenderer was not set
        DLLEXPORT TextRenderer* zTextRenderer() const;
        //! Returns the TextRenderer.
        //! \param index The index of the renderer to return
        //! \return 0 if the TextRenderer was not set
        DLLEXPORT TextRenderer* getTextRenderer(int index) const;
        //! Returns the TextRenderer without increased reference counter
        //! \param index The index of the renderer to return
        //! \return 0 if the TextRenderer was not set
        DLLEXPORT TextRenderer* zTextRenderer(int index) const;
        //! Returns the font size
        DLLEXPORT unsigned char getFontSize() const;
        //! Returns the font size
        //! \param index The index of the character
        DLLEXPORT unsigned char getFontSize(int index) const;
        //! Returns the font color in A8R8G8B8 format
        DLLEXPORT int getFontColor() const;
        //! Returns the font color in A8R8G8B8 format
        //! \param index The index of the character
        DLLEXPORT int getFontColor(int index) const;
        //! Returns the display character
        DLLEXPORT unsigned char getShowChar() const;
        //! Returns the cursor position
        DLLEXPORT int getCursorPos() const;
        //! Returns 1 if the character is selected
        //! \param index The index of the character
        DLLEXPORT bool isCharSelected(int index) const;
        //! Returns the index of the character located under the mouse
        //! \param mx The x position of the mouse relative to the text field
        //! position \param my The y position of the mouse relative to the
        //! text field position \return -1 if no character is at the position
        DLLEXPORT int getTextIndexAt(int mx, int my) const;
        //! Returns the index of the character before which the cursor is
        //! placed when clicking with the mouse \param mx The x position of
        //! the mouse relative to the text field position \param my The y
        //! position of the mouse relative to the text field position
        DLLEXPORT int getCurserPosAt(int mx, int my) const;
        //! Returns the style of a specific character
        //! \param index The index of the character
        DLLEXPORT TextStyle getTextStyle(int index) const;
        //! Returns the character sequence inserted after each line break
        //! when using the AutoLineBreak style
        DLLEXPORT Text getAutoLineBreakSpacing() const;
        //! Creates a complete copy of the text field that can be modified
        //! without affecting the original
        DLLEXPORT Drawable* duplicate() const override;
    };
} // namespace Framework