#ifndef Drawable_H
#define Drawable_H

#include <functional>

#include "Critical.h"
#include "KeyboardEvent.h"
#include "MouseEvent.h"
#include "Point.h"
#include "ReferenceCounter.h"

namespace Framework
{
    struct MouseEvent;    //! MouseEvent.h
    struct KeyboardEvent; //! KeyboardEvent.h
    class Image;          //! Image.h
    class Drawable;       //! From this file
    class ToolTip;        //! ToolTip.h
    class Screen;         //! Screen.h
    class Border;         //! Border.h
    class AlphaField;     //! AlphaField.h
    class VScrollBar;     //! Scroll.h
    class HScrollBar;     //! Scroll.h
    class Font;

    //! A drawing element for the 2D GUI Framework
    class Drawable : public virtual ReferenceCounter
    {
    public:
        class Style
        {
        public:
            //! If this style is set, the drawing is displayed when rendered
            static const __int64 Visible = 0x00001;
            //! If this style is set, the user can interact with the drawing
            static const __int64 Allowed = 0x00002;
            //! If this style is set, keyboard events are processed by the
            //! drawing
            static const __int64 Focus = 0x00004;
            //! If this style is set, mouse events are processed even when
            //! the object is not visible
            static const __int64 MEIgnoreVisible = 0x0800000000000000;
            //! If this style is set, mouse events are processed even when
            //! they are outside the drawing
            static const __int64 MEIgnoreInside = 0x2000000000000000;
            //! If this style is set, mouse events are processed even when
            //! they have already been processed by another drawing
            static const __int64 MEIgnoreProcessed = 0x4000000000000000;
            //! If this style is set, mouse events are processed even when
            //! they are outside the drawing to which this drawing belongs
            static const __int64 MEIgnoreParentInside = 0x8000000000000000;
        };

    protected:
        Point pos;
        Point gr;
        void* makParam;
        void* takParam;
        MouseAction mak;
        KeyboardAction tak;
        void* nmakParam;
        void* ntakParam;
        MouseAction nMak;
        KeyboardAction nTak;
        bool mouseIn;
        ReadWriteLock rwLock;
        ToolTip* toolTip;
        __int64 style;
        bool rend;
        std::function<bool(Drawable*, Point localPos)> onNeedToolTip;
        bool toolTipRequested;

        //! Processes a mouse event. Called automatically by the framework.
        //! \param me The mouse event \param userRet true if
        //! MouseEvent::processed should be set to true
        DLLEXPORT virtual void doMouseEvent(MouseEvent& me, bool userRet);

    public:
        //! Constructor
        DLLEXPORT Drawable();
        //! Destructor
        DLLEXPORT virtual ~Drawable();
        //! Specifies whether the drawing has changed since the last frame
        //! and needs to be redrawn
        DLLEXPORT void setRender();
        //! Sets the text that appears when the user hovers the mouse over
        //! the drawing for a longer time \param txt The text to display
        //! \param zScreen A pointer to the screen object without increased
        //! reference counter \param zFont A pointer to the font to use
        //! without increased reference counter
        DLLEXPORT void setToolTipText(
            const char* txt, Screen* zScreen, Font* zFont);
        //! Sets a function called when a tooltip is needed and none has
        //! been set yet \param initToolTip The function
        DLLEXPORT void setNeedToolTipEvent(
            std::function<bool(Drawable*, Point localPos)> onNeedTooltip);
        //! Sets the tooltip
        //! \param tt The tooltip
        DLLEXPORT void setToolTipZ(ToolTip* tt);
        //! returns a read lock for the drawing.
        DLLEXPORT Lock& readLock();
        //! returns a write lock for the drawing.
        DLLEXPORT Lock& writeLock();
        //! Sets the parameter passed to the callback function on a mouse event
        //! \param p The parameter
        DLLEXPORT void setMouseEventParameter(void* p);
        //! Sets the parameter passed to the callback function on a keyboard
        //! event
        //! \param p The parameter
        DLLEXPORT void setKeyboardEventParameter(void* p);
        //! Sets the callback function to be called on a mouse event.
        //! If the callback returns 0, or was not set, the mouse event
        //! is not further processed by the drawing. The standard function
        //! __ret1ME can be used, which is defined in MouseEvent.h and always
        //! returns 1 \param ak A pointer to the callback function
        DLLEXPORT void setMouseEvent(MouseAction ak);
        //! Adds a callback function to be called on a mouse event.
        //! If any callback returns 0, or none was set, the mouse event
        //! is not further processed by the drawing. The standard function
        //! __ret1ME can be used, which is defined in MouseEvent.h and always
        //! returns 1 \param ak A pointer to the callback function
        DLLEXPORT void addMouseEvent(MouseAction ak);
        //! Sets the callback function to be called on a keyboard event.
        //! If the callback returns 0, or was not set, the keyboard event
        //! is not further processed by the drawing. The standard function
        //! __ret1TE can be used, which is defined in KeyboardEvent.h and
        //! always returns 1. Other standard functions are _onlyNumbersTE and
        //! _onlyHexTE also from KeyboardEvent.h \param ak A pointer to
        //! the callback function
        DLLEXPORT void setKeyboardEvent(KeyboardAction ak);
        //! Adds a callback function to be called on a keyboard event.
        //! If any callback returns 0, or none was set, the keyboard event
        //! is not further processed by the drawing. The standard function
        //! __ret1TE can be used, which is defined in KeyboardEvent.h and
        //! always returns 1. Other standard functions are _onlyNumbersTE and
        //! _onlyHexTE also from KeyboardEvent.h \param ak A pointer to
        //! the callback function
        DLLEXPORT void addKeyboardEvent(KeyboardAction ak);
        //! Sets the parameter passed to the callback function on a mouse event
        //! that is called after the event was processed by the drawing
        //! \param p The parameter
        DLLEXPORT void setPostMouseEventParameter(void* p);
        //! Sets the parameter passed to the callback function on a keyboard
        //! event that is called after the event was processed by the drawing
        //! \param p The parameter
        DLLEXPORT void setPostKeyboardEventParameter(void* p);
        //! Sets the callback function to be called on a mouse event after
        //! the event has already been processed by the drawing. If the callback
        //! returns 1, or was not set, the mouse event will not be processed
        //! by any other drawing, such as ones behind this drawing. The standard
        //! function __ret1ME can be used, which is defined in MouseEvent.h
        //! and always returns 1 \param ak A pointer to the callback function
        DLLEXPORT void setPostMouseEvent(MouseAction ak);
        //! Sets the callback function to be called on a keyboard event after
        //! the event has already been processed by the drawing. If the callback
        //! returns 1, or was not set, the keyboard event will not be processed
        //! by any other drawing. The standard function __ret1TE can be used,
        //! which is defined in KeyboardEvent.h and always returns 1. Other
        //! standard functions are _onlyNumbersTE and _onlyHexTE also from
        //! KeyboardEvent.h \param ak A pointer to the callback function
        DLLEXPORT void setPostKeyboardEvent(KeyboardAction ak);
        //! Processes a mouse event. Called automatically by the framework.
        //! \param me The event
        DLLEXPORT virtual void doPublicMouseEvent(MouseEvent& me);
        //! Processes a keyboard event. Called automatically by the framework
        //! \param te The event
        DLLEXPORT virtual void doKeyboardEvent(KeyboardEvent& te);
        //! Processes the time elapsed since the last call of this function
        //! \param tickVal The elapsed time in seconds
        DLLEXPORT virtual bool tick(double tickval);
        //! Sets the position of the drawing
        //! \param pos The position in pixels
        DLLEXPORT void setPosition(const Point& pos);
        //! Sets the X position of the drawing
        //! \param xPos The position in pixels
        DLLEXPORT void setX(int xPos);
        //! Sets the Y position of the drawing
        //! \param yPos The position in pixels
        DLLEXPORT void setY(int yPos);
        //! Sets the size of the drawing
        //! \param gr A point with x as width and y as height in pixels
        DLLEXPORT void setSize(const Point& gr);
        //! Sets the position of the drawing
        //! \param x The X position in pixels
        //! \param y The Y position in pixels
        DLLEXPORT void setPosition(int x, int y);
        //! Sets the size of the drawing
        //! \param br The width in pixels
        //! \param height The height in pixels
        DLLEXPORT void setSize(int br, int height);
        //! Set the width in pixel
        //! \param width the width
        DLLEXPORT void setWidth(int width);
        //! Set the height in pixel
        //! \param width the height
        DLLEXPORT void setHeight(int height);
        //! Sets the style of the drawing
        //! \param style The new style consisting of flags from the
        //! corresponding Style class
        DLLEXPORT void setStyle(__int64 style);
        //! Sets the style of the drawing
        //! \param style All style flags to be modified
        //! \param add_remove 1 if the style should be added. 0 if the style
        //! should be removed
        DLLEXPORT void setStyle(__int64 style, bool add_remove);
        //! Adds style flags
        //! \param style The style to add
        DLLEXPORT void addStyle(__int64 style);
        //! Removes style flags
        //! \param style The style to remove
        DLLEXPORT void removeStyle(__int64 style);
        //! Renders the drawing into a specific image
        //! \param zRObj The image to draw into
        DLLEXPORT virtual void render(Image& zRObj);
        //! Returns whether a callback function for mouse events was set
        DLLEXPORT bool hasMouseEvent() const;
        //! Returns whether a callback function for keyboard events was set
        DLLEXPORT bool hasKeyboardEvent() const;
        //! Returns the position of the drawing in pixels
        DLLEXPORT const Point& getPosition() const;
        //! Returns the size of the drawing in pixels. x for width and y
        //! for height
        DLLEXPORT const Point& getSize() const;
        //! Returns the width of the drawing in pixels
        DLLEXPORT int getWidth() const;
        //! Returns the height of the drawing in pixels
        DLLEXPORT int getHeight() const;
        //! Returns the inner width of the drawing in pixels
        DLLEXPORT virtual int getInnerWidth() const;
        //! Returns the inner height of the drawing in pixels
        DLLEXPORT virtual int getInnerHeight() const;
        //! Returns the X position of the drawing in pixels
        DLLEXPORT int getX() const;
        //! Returns the Y position of the drawing in pixels
        DLLEXPORT int getY() const;
        //! Checks whether a point is inside this object
        //! \param p The point
        //! \return 1 if the point is inside, 0 otherwise
        DLLEXPORT virtual bool isPointInside(Point p) const;
        //! Checks whether a point is inside this object
        //! \param x The x coordinate of the point
        //! \param y The y coordinate of the point
        //! \return 1 if the point is inside, 0 otherwise
        DLLEXPORT virtual bool isPointInside(int x, int y) const;
        //! Returns a pointer to the tooltip object, if it is used
        DLLEXPORT ToolTip* getToolTip() const;
        //! Returns a pointer to the tooltip object without increased
        //! reference counter, if it is used
        DLLEXPORT ToolTip* zToolTip() const;
        //! Returns whether specific styles are set
        //! \param style The styles to check
        //! \return 1 if all styles in style are set
        DLLEXPORT bool hasStyle(__int64 style) const;
        //! Returns whether specific styles are not set
        //! \param style The styles to check
        //! \return 1 if all styles in style are not set
        DLLEXPORT bool hasStyleNot(__int64 style) const;
        //! returns all currently active styles
        DLLEXPORT __int64 getStyles() const;
        //! Copies the complete drawing so it can be modified without
        //! affecting the original
        DLLEXPORT virtual Drawable* duplicate() const;
    };

    //! A drawing element for the 2D GUI Framework with background, border
    //! and scroll bars
    class DrawableBackground : public Drawable
    {
    public:
        class Style : public Drawable::Style
        {
        public:
            //! If this flag is set, the drawing gets a border
            static const __int64 Border = 0x00010;
            //! If this flag is set, the drawing gets a background
            static const __int64 Background = 0x00020;
            //! If this flag is set, the background is transparent.
            //! Requires flag Background
            static const __int64 BAlpha = 0x00040;
            //! If this flag is set, an image is used as background.
            //! Requires flag Background
            static const __int64 BImage = 0x00080;
            //! If this flag is set, a color gradient appears as border
            static const __int64 Buffered = 0x00100;
            //! If this flag is set, a scrollbar appears at the right edge
            static const __int64 VScroll = 0x00200;
            //! If this flag is set, a scrollbar appears at the bottom edge
            static const __int64 HScroll = 0x00400;
            //! If this flag is set, the image is scaled
            static const __int64 BImageScale = 0x00800;
        };

    protected:
        int backgroundColor;
        Border* border;
        Image* backgroundImage;
        AlphaField* backgroundFeld;
        VScrollBar* vertikalScrollBar;
        HScrollBar* horizontalScrollBar;
        Point innenPosition;
        Point innenSize;

    protected:
        //! Processes mouse messages
        //! \param me The event triggered by the mouse input
        DLLEXPORT void doMouseEvent(MouseEvent& me, bool userRet) override;

    public:
        //! Constructor
        DLLEXPORT DrawableBackground();
        //! Destructor
        DLLEXPORT virtual ~DrawableBackground();
        //! Sets the background image (requires drawing flag:
        //! Style::Background, HImage) \param bild The image is copied and used
        //! as background image
        DLLEXPORT void setBackgroundImage(Image* bild);
        //! Sets a pointer to the background image (requires drawing flag:
        //! Style::Background) \param bild The image is used without copying
        DLLEXPORT void setBackgroundImageZ(Image* bild);
        //! Sets the background color (requires drawing flag:
        //! Style::Background) \param fc The background color in A8R8G8B8 format
        DLLEXPORT void setBackgroundColor(int fc);
        //! Sets a pointer to the AlphaField (requires drawing flag:
        //! Style::Buffered) \param buff The AlphaField to draw over the
        //! background
        DLLEXPORT void setAlphaFieldZ(AlphaField* buff);
        //! Sets the strength of the AlphaField (requires drawing flag:
        //! Style::Buffered) \param st The strength of the AlphaField drawn
        //! over the background
        DLLEXPORT void setAlphaFieldStrength(int st);
        //! Sets the color of the AlphaField (requires drawing flag:
        //! Style::Buffered) \param fc The color of the AlphaField drawn
        //! over the background
        DLLEXPORT void setAlphaFieldColor(int fc);
        //! Sets a pointer to the line border drawn around the text field
        //! (requires drawing flag: Style::Border)
        //! \param ram The border
        DLLEXPORT void setBorderZ(Border* ram);
        //! Sets the width of the line border (requires drawing flag:
        //! Style::Border) \param br The width in pixels
        DLLEXPORT void setBorderWidth(int br);
        //! Sets the color of the line border (requires drawing flag:
        //! Style::Border) \param fc The color in A8R8G8B8 format
        DLLEXPORT void setBorderColor(int fc);
        //! Sets the scroll speed of the vertical scroll bar (requires drawing
        //! flag: Style::VScroll) \param ks The scroll speed in pixels per mouse
        //! click
        DLLEXPORT void setVerticalClickScroll(int ks);
        //! Scrolls to a specific position on the vertical scroll bar
        //! (requires drawing flag: Style::VScroll) \param pos The scroll offset
        //! in pixels
        DLLEXPORT void setVerticalScrollPos(int pos);
        //! Sets the color of the vertical scroll bar (requires drawing flag:
        //! Style::VScroll) \param f The foreground color in A8R8G8B8 format
        //! \param bgF The background color in A8R8G8B8 format
        DLLEXPORT void setVerticalScrollColor(int f, int bgF);
        //! Sets the scroll speed of the horizontal scroll bar (requires drawing
        //! flag: Style::HScroll) \param ks The scroll speed in pixels per mouse
        //! click
        DLLEXPORT void setHorizontalClickScroll(int ks);
        //! Scrolls to a specific position on the horizontal scroll bar
        //! (requires drawing flag: Style::HScroll) \param pos The scroll offset
        //! in pixels
        DLLEXPORT void setHorizontalScrollPos(int pos);
        //! Sets the color of the horizontal scroll bar (requires drawing flag:
        //! Style::HScroll) \param f The foreground color in A8R8G8B8 format
        //! \param bgF The background color in A8R8G8B8 format
        DLLEXPORT void setHorizontalScrollColor(int f, int bgF);
        //! 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 inner width of the drawing in pixels
        DLLEXPORT virtual int getInnerWidth() const override;
        //! Returns the inner height of the drawing in pixels
        DLLEXPORT virtual int getInnerHeight() const override;
        //! Returns the background image.
        //! \return 0 if no background image is used
        DLLEXPORT Image* getBackgroundImage() const;
        //! Returns the background image without increased reference counter.
        //! \return 0 if no background image is used
        DLLEXPORT Image* zBackgroundImage() const;
        //! Returns the background color in A8R8G8B8 format
        DLLEXPORT int getBackgroundColor() const;
        //! Returns the AlphaField drawn over the background.
        //! \return 0 if the AlphaField was not defined
        DLLEXPORT AlphaField* getAlphaField() const;
        //! Returns the AlphaField without increased reference counter drawn
        //! over the background. \return 0 if the AlphaField was not defined
        DLLEXPORT AlphaField* zAlphaField() const;
        //! Returns the strength of the AlphaField
        DLLEXPORT int getAlphaFieldStrength() const;
        //! Returns the color of the AlphaField in A8R8G8B8 format
        DLLEXPORT int getAlphaFieldColor() const;
        //! Returns the border
        //! \return 0 if no border was defined
        DLLEXPORT Border* getBorder() const;
        //! Returns the border without increased reference counter
        //! \return 0 if no border was defined
        DLLEXPORT Border* zBorder() const;
        //! Returns the width of the border in pixels
        DLLEXPORT int getBorderWidth() const;
        //! Returns the color of the border in A8R8G8B8 format
        DLLEXPORT int getBorderColor() const;
        //! Returns the scroll speed of the vertical scroll bar
        DLLEXPORT int getVerticalClickScroll() const;
        //! Returns the scroll position of the vertical scroll bar
        DLLEXPORT int getVerticalScrollPos() const;
        //! Returns the color of the vertical scroll bar in A8R8G8B8 format
        DLLEXPORT int getVerticalScrollColor() const;
        //! Returns the background color of the vertical scroll bar in A8R8G8B8
        //! format
        DLLEXPORT int getVerticalScrollBackground() const;
        //! Returns the scroll speed of the horizontal scroll bar
        DLLEXPORT int getHorizontalClickScroll() const;
        //! Returns the scroll position of the horizontal scroll bar
        DLLEXPORT int getHorizontalScrollPos() const;
        //! Returns the color of the horizontal scroll bar in A8R8G8B8 format
        DLLEXPORT int getHorizontalScrollColor() const;
        //! Returns the background color of the horizontal scroll bar in
        //! A8R8G8B8 format
        DLLEXPORT int getHorizontalScrollBackground() const;
        //! Creates a copy of the drawing that can be modified without
        //! affecting the original
        DLLEXPORT virtual Drawable* duplicate() const;
    };
} // namespace Framework

#endif