#pragma once

#include "Array.h"
#include "Trie.h"
#include "UIInitialization.h"
#include "XML.h"
#include "Drawing.h"

namespace Framework
{
    class Text;
    class ObjTable;
    class Font;
    class Screen;
    class UIMLContainer;

    /**
      creates a Drawable that views an element of the uiml

      tag: defined by subclasses;

      basic attributes:
        - (__int64) style: sets the style. Default: TextField::Style::TextField
          + 0x...: will be interpreted as hex
          # ...: will be interpreted as normal __int64
        - (string) text-align-horizontal:
          + center: adds the style TextField::Style::HCenter
        - (string) text-align-vertical:
          + center: adds the style TextField::Style::VCenter
        - (int) font-size: sets the font size
        - (int) text-color: sets the text color format: 0xAARRGGBB
        - disabled: removes the style TextField::Style::Editierbar
        - (string) width: sets the with
          + (int)%: specifies the width in % of the parent element
          + (int): specifies the width in pixel
          + (expression): calculates the width from an expression that contains
            values concatenated by the operators +, -, / or *
          + auto: attribute will be ignored here and may be handled by the
            subclasses
        - (string) height: sets the with
          + (int)%: specifies the width in % of the parent element
          + (int): specifies the width in pixel
          + (expression): calculates the width from an expression that contains
            values concatenated by the operators +, -, / or *
          + auto: attribute will be ignored here and may be handled by the
            subclasses
        - (string) align-x: sets the x coordinate of the view. The value of
          margin-x will be added
          + start: 0
          + end: parentWidth
          + center: parentWidth / 2 - width / 2
          + id of other element: other element x
        - (string) margin-x: only available if align-x is specified.
          + (int)%: specifies the distance to its x aligned coordinate in
            percentage of parentWidth
          + (int): specifies the distance to its x aligned coordinate in pixel
          + (expression): calculates the distance from an expression that
            contains values concatenated by the operators +, -, / or *
        - (string) align-y: sets the y coordinate of the view. The value of
          margin-y will be added
          + start: 0
          + end: parentWidth
          + center: parentHeight / 2 - height / 2
          + id of other element: other element y
        - (string) margin-y: only available if align-y is specified.
          + (int)%: specifies the distance to its y aligned coordinate in
            percentage of parentHeight
          + (int): specifies the distance to its y aligned coordinate in pixel
          + (expression): calculates the distance from an expression that
            contains values concatenated by the operators +, -, / or *
        - (string) align-left: sets the x coordinate of the view. The value of
          margin-left will be added. Will be ignored when align-x is present.
          + start: 0
          + end: parentWidth
          + center: parentWidth / 2 - width / 2
          + id of other element: other element x + other element width
        - (string) margin-left: only available if align-left is specified.
          + (int)%: specifies the distance to its left aligned coordinate in
            percentage of parentWidth
          + (int): specifies the distance to its left aligned coordinate in
            pixel
          + (expression): calculates the distance from an expression that
            contains values concatenated by the operators +, -, / or *
        - (string) align-right: sets the x coordinate of the view. The value of
          margin-right will be subtracted. Will be ignored when align-x or
          align-left is present.
          + start: -width
          + end: parentWidth - width
          + center: parentWidth / 2 - width / 2
          + id of other element: other element x - width
        - (string) margin-right: only available if align-right is specified.
          + (int)%: specifies the distance to its right aligned coordinate in
            percentage of parentWidth
          + (int): specifies the distance to its right aligned coordinate in
            pixel
          + (expression): calculates the distance from an expression that
            contains values concatenated by the operators +, -, / or *
        - (string) align-top: sets the y coordinate of the view. The value of
          margin-top will be added. Will be ignored when align-y is present.
          + start: 0
          + end: parentHeight
          + center: parentHeight / 2 - height / 2
          + id of other element: other element y + other element height
        - (string) margin-top: only available if align-top is specified.
          + (int)%: specifies the distance to its top aligned coordinate in
            percentage of parentHeight
          + (int): specifies the distance to its top aligned coordinate in pixel
          + (expression): calculates the distance from an expression that
            contains values concatenated by the operators +, -, / or *
        - (string) align-bottom: sets the y coordinate of the view. The value of
          margin-bottom will be subtracted. Will be ignored when align-y or
          align-top is present.
          + start: -height
          + end: parentHeight - height
          + center: parentHeight / 2 - height / 2
          + id of other element: other element y - height
        - (string) margin-bottom: only available if align-bottom is specified.
          + (int)%: specifies the distance to its bottom aligned coordinate in
            percentage of parentHeight
          + (int): specifies the distance to its bottom aligned coordinate in
            pixel
          + (expression): calculates the distance from an expression that
            contains values concatenated by the operators +, -, / or *
        - (string) x: sets the x coordinate of the view. overwrites align-left
          and align-right and align-x
          + (int)%: specifies the x coordinate in percentage of parentWidth
          + (int): specifies the x coordinate in pixel
          + (expression): calculates the x coordinate from an expression that
            contains values concatenated by the operators +, -, / or *
        - (string) y: sets the y coordinate of the view. overwrites align-top
          and align-bottom and align-y
          + (int)%: specifies the y coordinate in percentage of parentHeight
          + (int): specifies the y coordinate in pixel
          + (expression): calculates the y from an expression that contains
            values concatenated by the operators +, -, / or *

      additional attributes if the created view is an instance of
      DrawableBackground:
        - (int) border: width of the border in pixel
        - (int) border-color: color of the border format: 0xAARRGGBB
    */
    class UIMLElement : public virtual ReferenceCounter
    {
    public:
        DLLEXPORT UIMLElement();
        DLLEXPORT virtual ~UIMLElement();
        //! Checks whether this UIML element is responsible for a specific xml element
        virtual bool isApplicableFor(XML::Element& element) = 0;
        //! Creates a new Drawable for a given xml element
        virtual Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory)
            = 0;
        //! Updates a Drawable with the data of an xml element. Returns
        //! false if the Drawable could not be updated and needs to be recreated
        virtual bool updateElement(
            XML::Element& element, Drawable& z, UIMLContainer& generalFactory)
            = 0;
        //! Applies the layout parameters to a Drawable
        DLLEXPORT virtual void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter);
        /// <summary>
        /// calculates the size based on a given attribute value. Allowed values
        /// are expression with +, -, * or / operators and percentages of
        /// parentSize. uses 0 for each invalid part of the expression
        /// </summary>
        /// <param name="attributeValue">the expression that should be
        /// evaluated</param> <param name="parentSize">the size of the parent in
        /// pixel</param> <returns>the resulting size in pixel</returns>
        DLLEXPORT int calculateSize(Text attributeValue, int parentSize);
        // returns an iterator for all children that are elements and need to be
        // layouted
        //  \param element the current element
        DLLEXPORT virtual XML::Editor getChildren(XML::Element& element);
    };

    class UIMLContainer : public UIMLElement
    {
    public:
        DLLEXPORT UIMLContainer();
        DLLEXPORT virtual ~UIMLContainer();
        virtual Drawable* zDrawableById(const char* id) = 0;
        virtual Drawable* getDrawableById(const char* id) = 0;
        virtual Text getDrawableId(Drawable& z) = 0;
        virtual void removeDrawable(Drawable& z) = 0;
        virtual bool registerDrawable(const char* id, Drawable* z) = 0;
        virtual const UIInit& getFactory() = 0;
    };

    /**
      creates a TextField instance with style TextField::Style::TextField

      tag: &lt;textfield&gt;

      additional attributes to UIMLElement:
        - (__int64) style: sets the style. Default: TextField::Style::TextField
        - (string) text-align-horizontal:
          + center: adds the style TextField::Style::HCenter
        - (string) text-align-vertical:
          + center: adds the style TextField::Style::VCenter
        - (int) font-size: sets the font size
        - (int) text-color: sets the text color. format: 0xAARRGGBB
        - disabled: removes the style TextField::Style::Editierbar
        - (string) width: sets the with
          + auto: setzt the width to the calculated needed width
        - (string) height: sets the height
          + auto: setzt the height to the calculated needed height
        - (string) vScroll: adds a vertical scroll bar
          + auto: the max vertical scroll size is set to the needed text height
            and the click scroll per pixel will be set to font size
        - (int) hScroll: adds a hotizontal scroll bar
          + auto: the max horizontal scroll size is set to the needed text width
            and the click scroll per pixel will be set to font size
    */
    class UIMLTextField : public UIMLElement
    {
    public:
        DLLEXPORT UIMLTextField();
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
    };

    class UIMLButton : public UIMLElement
    {
    public:
        DLLEXPORT UIMLButton();
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
    };

    class UIMLCheck : public UIMLElement
    {
    public:
        DLLEXPORT UIMLCheck();
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
    };

    /**
      creates a TextField instance with style TextField::Style::Text

      tag: &lt;text&gt;

      additional attributes to UIMLElement:
        - (__int64) style: sets the style. Default: TextField::Style::Text
        - (string) text-align-horizontal:
          + center: adds the style TextField::Style::HCenter
        - (string) text-align-vertical:
          + center: adds the style TextField::Style::VCenter
        - (int) font-size: sets the font size
        - (int) text-color: sets the text color. format: 0xAARRGGBB
        - disabled: removes the style TextField::Style::Editierbar
        - (string) width: sets the with
          + auto: setzt the width to the calculated needed width
        - (string) height: sets the height
          + auto: setzt the height to the calculated needed height
        - (string) vScroll: adds a vertical scroll bar
          + auto: the max vertical scroll size is set to the needed text height
            and the click scroll per pixel will be set to font size
        - (int) hScroll: adds a hotizontal scroll bar
          + auto: the max horizontal scroll size is set to the needed text width
            and the click scroll per pixel will be set to font size
    */
    class UIMLText : public UIMLElement
    {
    public:
        DLLEXPORT UIMLText();
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
    };

    /**
      creates a TextField instance with style TextField::Style::TextGebiet

      tag: &lt;textarea&gt;

      additional attributes to UIMLElement:
        - (__int64) style: sets the style. Default: TextField::Style::TextGebiet
        - (string) text-align-horizontal:
          + center: adds the style TextField::Style::HCenter
        - (string) text-align-vertical:
          + center: adds the style TextField::Style::VCenter
        - (int) font-size: sets the font size
        - (int) text-color: sets the text color. format: 0xAARRGGBB
        - disabled: removes the style TextField::Style::Editierbar
        - (string) width: sets the with
          + auto: setzt the width to the calculated needed width
        - (string) height: sets the height
          + auto: setzt the height to the calculated needed height
        - (string) vScroll: adds a vertical scroll bar
          + auto: the max vertical scroll size is set to the needed text height
            and the click scroll per pixel will be set to font size
        - (int) hScroll: adds a hotizontal scroll bar
          + auto: the max horizontal scroll size is set to the needed text width
            and the click scroll per pixel will be set to font size
    */
    class UIMLTextArea : public UIMLElement
    {
    public:
        DLLEXPORT UIMLTextArea();
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
    };

    class UIMLTable : public UIMLElement
    {
    public:
        DLLEXPORT UIMLTable();
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
        virtual XML::Editor getChildren(XML::Element& element) override;
    };

    /**
      creates a Window instance

      tag: &lt;frame&gt;

      additional attributes to UIMLElement:
        - (string) title: the title of the window
        - (int) title-height: sets the height of the title
        - (int) title-font-size: sets the font size of the title
        - (int) title-text-color: sets the color of the title. format:
          0xAARRGGBB
        - (string) display: specifies the position of child elements
          + column: sets the position of child elements vertically below each
            other. any align attribute will overwrite this behaviour.
          + row: sets the position of the child elements horizontally beneath
            each other. any align attribute will overwrite this behaviour.
        - (int) gap: the space between each child element. Only valid if display
          is specified.
          + (int)%: specifies the y coordinate in percentage of parentHeight
          + (int): specifies the y coordinate in pixel
          + (expression): calculates the width from an expression that contains
            values concatenated by the operators +, -, / or *
        - (string) vScroll: adds a vertical scroll bar
          + auto: the max vertical scroll size is set to the needed child
      elements height
        - (int) hScroll: adds a hotizontal scroll bar
          + auto: the max horizontal scroll size is set to the needed child
      elements width
        - (string) width: sets the with
          + auto: setzt the width to the calculated needed width
        - (string) height: sets the height
          + auto: setzt the height to the calculated needed height
    */
    class UIMLFrame : public UIMLElement
    {
    public:
        DLLEXPORT UIMLFrame();
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
    };

    /**
      Converts uiml views to Drawable implementations and renders them.
      The attribute id of each element has to be unique or absent.
      All implementations of UIMLElement in the framework are supported by
      default. Custom elements can be added by calling addKnownElement with a
      custom UIMLElement implementation. Look at the documentation of specific
      UIMLElement implementations to view all supported attributes.

      example:
        \code
        <view>
          <textfield id="element_at_top_left" align-left="start",
              align-top="start", margin-left="5", margin-top="5" width="90%"
              height="30"/>
          <table id="element_below_textfield" align-left="start",
              aliign-top="element_at_top_left", margin-left="5", margin-top="5"
              width="90%" height="300">
            <tr>
              <button id="a_button_in_a_table"/>
              <textfield id="a_textfield_in_a_table"/>
            </tr>
          </table>
        </view>
        \endcode
    */
    class UIMLView : public DrawableBackground,
                     public UIMLContainer
    {
    public:
        class Style : public DrawableBackground::Style
        {
        public:
            /// <summary>
            /// if this style is set, then the mousee event action will be
            /// overwritten for all member views. The onMemberMouseEvent
            /// function will be executed instead. The style needs to be set
            /// before the uiml is parsed
            /// </summary>
            static const int GlobalMouseEvent = 0x01000;
            /// <summary>
            /// if this style is set, then the keyvord event action will be
            /// overwritten for all member views. The onMemberKeybordEvent
            /// function will be executed instead. The style needs to be set
            /// before the uiml is parsed
            /// </summary>
            static const int GlobalTastaturEvent = 0x01000;
        };

    private:
        RCArray<UIMLElement> knownElements;
        UIInit init;
        RCTrie<Drawable>* members;
        Array<Drawable*> memberList;
        RCArray<Text> idList;
        XML::Element* dom;
        int nextId;
        std::function<bool(
            XML::Element& element, Drawable& member, MouseEvent me)>
            onMemberMouseEvent;
        std::function<bool(
            XML::Element& element, Drawable& member, KeyboardEvent te)>
            onMemberKeyboardEvent;

        //! Processes a mouse event. Called automatically by the framework.
        //! \param me The event
        DLLEXPORT virtual void doMouseEvent(
            MouseEvent& me, bool userRet) override;

    public:
        //! Creates a UIML View
        DLLEXPORT UIMLView();
        //! Creates a UIML View from a UIML text
        //! \param uiml An xml element according to the KSG UIML standard
        //! \param init The UI initialization settings
        DLLEXPORT UIMLView(XML::Element* uiml, UIInit& init);
        //! Creates a UIML View from a UIML text
        //! \param uiml An xml text according to the KSG UIML standard
        //! \param init The UI initialization settings
        DLLEXPORT UIMLView(Text uiml, UIInit& init);
        DLLEXPORT ~UIMLView();
        /// <summary>
        /// sets a function that is executed if a mouse event on a member view
        /// occures and the style Style::GlobalMouseEvent is set.
        /// The mouse event will be ignored by the view if false is returned.
        /// </summary>
        /// <param name="onEventAction">the function to execute if a mouse event
        /// occures</param>
        DLLEXPORT void setOnMemberMouseEvent(std::function<bool(
                XML::Element& element, Drawable& member, MouseEvent me)>
                onEventAction);
        /// <summary>
        /// sets a function that is executed if a mouse event on a member view
        /// occures and the style Style::GlobalTastaturEvent is set.
        /// The mouse event will be ignored by the view if false is returned.
        /// </summary>
        /// <param name="onEventAction">the function to execute if a mouse event
        /// occures</param>
        DLLEXPORT void setOnMemberKeyboardEvent(std::function<bool(
                XML::Element& element, Drawable& member, KeyboardEvent te)>
                onEventAction);
        //! Removes all known elements that can be used in uiml
        DLLEXPORT void removeAllKnownElements();
        //! Adds a new known element that can then be used in uiml
        DLLEXPORT void addKnownElement(UIMLElement* element);
        //! Checks whether an xml element is a known uiml element
        DLLEXPORT bool isKnownElement(XML::Element* zElement);
        //! Sets the content of the view
        //! \param uiml An xml element according to the KSG UIML standard
        DLLEXPORT void setUIML(XML::Element* uiml);
        //! Sets the content of the view
        //! \param uiml An xml text according to the KSG UIML standard
        DLLEXPORT void setUIML(Text uiml);
        //! Updates all drawings according to the specifications in UIML.
        //! Drawings of removed elements are removed and new drawings
        //! for new elements are created
        DLLEXPORT void update();
        //! Updates size and position of all drawings according to the
        //! specifications in UIML
        DLLEXPORT void layout();
        //! Adds an element
        //! \param uiml An xml text according to the KSG UIML standard representing
        //! the new object \return id of the new element
        DLLEXPORT Text addMember(Text uiml);
        //! Adds an element to a parent element (currently only works
        //! with frame objects) \param uiml An xml text according to the KSG UIML
        //! standard representing the new object \return id of the new element
        DLLEXPORT Text addMember(Text uiml, Text parentId);
        //! Removes an element
        //! \param id The id of the element
        DLLEXPORT void removeMember(Text id);
        //! Returns a drawing that has a specific id in uiml
        //! \param id The id of the drawing
        DLLEXPORT Drawable* zDrawableById(const char* id) override;
        //! Returns a drawing that has a specific id in uiml
        //! \param id The id of the drawing
        DLLEXPORT Drawable* getDrawableById(const char* id) override;
        //! Processes a keyboard event. Called automatically by the framework
        //! \param te The event
        DLLEXPORT virtual void doKeyboardEvent(KeyboardEvent& te);
        //! Updates the drawing background
        //! \param tickVal The elapsed time in seconds since the last call
        //! of this function \return 1 if the image needs to be redrawn. 0 otherwise
        DLLEXPORT bool tick(double tickVal) override;
        //! Renders the background of a drawing to rObj
        DLLEXPORT void render(Image& rObj) override;
        //! Returns the DOM tree without increased reference counter
        //! Changes to the DOM tree should be avoided (only changes to
        //! attributes of individual elements are allowed)
        DLLEXPORT XML::Element* zDom() const;
        //! Returns the DOM tree
        //! Changes to the DOM tree should be avoided (only changes to
        //! attributes of individual elements are allowed)
        DLLEXPORT XML::Element* getDom() const;
        DLLEXPORT bool isApplicableFor(XML::Element& element) override;
        DLLEXPORT Drawable* parseElement(
            XML::Element& element, UIMLContainer& generalFactory) override;
        DLLEXPORT bool updateElement(XML::Element& element,
            Drawable& z,
            UIMLContainer& generalFactory) override;
        DLLEXPORT void layout(XML::Element& element,
            Drawable& z,
            int pWidth,
            int pHeight,
            UIMLContainer& generalLayouter) override;
        DLLEXPORT Text getDrawableId(Drawable& z) override;
        DLLEXPORT void removeDrawable(Drawable& z) override;
        DLLEXPORT bool registerDrawable(const char* id, Drawable* z) override;
        DLLEXPORT const UIInit& getFactory() override;
        //! calculates the needed size for all content elements to be visible
        DLLEXPORT Point calculateContentSize();
    };
} // namespace Framework