/**
 * @file GuiElements.h
 * @author Petr Egert (egertpet@fel.cvut.cz)
 * @brief Contains all GUI element classes declarations
 */

#ifndef GUIELEMENTS_H
#define GUIELEMENTS_H

#include "Matrix.h"
#include "Gui.h"
#include "Globals.h"
#include "Texture.h"
#include "Font.h"
#include "Vector.h"

#include <string>
#define _USE_MATH_DEFINES
#include <math.h>

namespace RD
{
    class Gui;

    /**
     * @brief Base abstract class representing a GUI element
     *
     * The pivot point is located at the top-left corner (ie. position (0.0, 0.0) means the top left corner of the element is located at (0.0, 0.0)
     * All the transformation are relative to the parent element (ie. child element with size (1.0, 1.0) has the equal size as it's parent)
     */
    class GuiElement
    {
        public:
            ///Default constructor - sets position (0.0, 0.0), size (1.0, 1.0), rotation (0.0) and rotation center (0.0, 0.0)
            GuiElement() : hasCustomTransform(false), pos(0.0, 0.0), size(1.0, 1.0), rot(0.0), rotCenter(0.0, 0.0), gui(NULL), parent(NULL){}

            ///Default destructor, currently does nothing
            virtual ~GuiElement(){}

            ///Performs class initialization, currently does nothing
            virtual void Init(){}

            virtual void Release();
            virtual void Update();
            virtual void Render();

            void ReleaseChildren();
            void UpdateChildren();
            void RenderChildren();

            void AddChild(GuiElement *element);

            void SetPos(const vec2 &pos);
            void SetSize(const vec2 &size);
            void SetRot(float rot);
            void SetRotCenter(const vec2 &rotCenter);

            void SetGui(Gui *gui);

            void SetTransform(const mat44 &transform);

            virtual GuiElement *Clone();

            mat44 transform;            ///< Element's transformation matrix
            bool hasCustomTransform;    ///< If true, the tranformation matrix does not get automatically updated

            vec2 pos;           ///< Position of the element
            vec2 size;          ///< Size of the element
            float rot;          ///< Rotation of the element in radians
            vec2 rotCenter;     ///< Center point used for rotation of the element

        protected:
            ///Definition of a container used to store children elements
            typedef std::list<GuiElement*> ChildrenContainer;

            virtual GuiElement *CloneSelf();
            bool CloneChildren(GuiElement *target);
            virtual void OnAfterChildClone(GuiElement *target, GuiElement *myChild, GuiElement *targetChild);

            virtual void UpdateMatrix(bool recursive = true);

            Gui *gui;    ///< GUI the element belongs to

            GuiElement *parent;      ///< Pointer to the parent element (if any) or NULL
            ChildrenContainer children; ///< Container holding all the children elements

        private:


    };

    /**
     * @brief Class representing a static image GUI element
     *
     * The image covers the whole area of the element
     */
    class GuiElementImage : public GuiElement
    {
        public:
            ///Default constructor, sets texture to NULL
            GuiElementImage() : texture(NULL){};

            ///Default destructor, currently does nothing
            ~GuiElementImage(){};

            void Render();

            void SetTexture(Texture *texture);

        protected:
            GuiElement *CloneSelf();

        private:
            Texture *texture;  ///< Texture representing the image of the element

    };

    /**
     * @brief Class representing a basic text GUI element
     *
     * Position specifies the top left point from which the text should be drawn (ie the top-left point of the first character of the text)
     * Size specifies the size of a single character, not the whole text
     */
    class GuiElementText : public GuiElement
    {
        public:
            ///Default contructor, sets font to NULL
            GuiElementText() : font(NULL){};

            ///Default destructor, currently does nothing
            virtual ~GuiElementText(){};

            virtual void Render();

            void SetFont(Font *font);
            void SetText(const std::string &text);

        protected:
            virtual GuiElement *CloneSelf();

            std::string text;   ///< Current text
            Font *font;      ///< Font used to draw the text

        private:

    };

    /**
     * @brief Class representing a text GUI element used specifically for showing numbers
     * @see GuiElementText
     */
    class GuiElementTextNumber : public GuiElementText
    {
        public:
            ///Default constructor, sets value to 0.0 and precision to 0 (no decimals)
            GuiElementTextNumber() : value(0.0), precision(0){}

            ///Default destructor, currently does nothing
            virtual ~GuiElementTextNumber(){}

            void SetValue(float value);
            void SetPrecision(int precision);

        protected:
            virtual GuiElement *CloneSelf();
            virtual void UpdateValue();

        private:
            float value;    ///< Current value
            int precision;  ///< Precision with which the value is shown (decimal spaces)
    };

    /**
     * @brief Class representing a text GUI element used specifically for showing 3D vectors
     *
     * Similar to GuiElementTextNumber, but for a 3D vector
     * Space is used as a separator between the individual components representations
     * @see GuiElementText
     * @see GuiElementTextNumber
     */
    class GuiElementTextVec3 : public GuiElementText
    {
        public:
            ///Default constructor, sets value to (0.0, 0.0, 0.0) and precision to 0 (no decimals)
            GuiElementTextVec3() : precision(0){};
            virtual ~GuiElementTextVec3(){};

            void SetValue(const vec3 &value);
            void SetPrecision(int precision);

        protected:
            virtual GuiElement *CloneSelf();
            virtual void UpdateValue();

        private:
            vec3 value;     ///< Current value
            int precision;  ///< Precision with which the value is shown (decimal spaces)
    };

    /**
     * @brief Class representing a dial GUI element
     *
     * Composed of two individual image elements:
     * baseElem - representing the base surface (ie. the actual dial)
     * needleElem - representing the needle pointing to the given value
     *
     * Transformation of the needle element (mainly it's scale and rotation center) should be set to proper values manually
     *
     * The rotation range (RotRange) is used to set the rotation of the needle element when pointing to the minimal and maximal value
     */
    class GuiElementDial : public GuiElement
    {
        public:
            ///Default constructor, sets range to 0.0 - 1.0, rotRange to 0.0 - 2*PI (360 degrees) and value to 0.0
            GuiElementDial() : baseElem(NULL), needleElem(NULL), range(0.0, 1.0), rotRange(0.0, M_2_PI), value(0.0){}

            ///Default destructor, currently does nothing
            virtual ~GuiElementDial(){}

            virtual void Init();
            virtual void Render();

            virtual void SetRange(const vec2 &range);
            virtual void SetRotRange(const vec2 &rotRange);
            virtual void SetValue(float value);

            GuiElementImage *baseElem;   ///< Element representing the dial background
            GuiElementImage *needleElem; ///< Element representing the needle pointing to current value

        protected:
            friend class GuiElementDialText;

            virtual GuiElement *CloneSelf();
            virtual void OnAfterChildClone(GuiElement *target, GuiElement *myChild, GuiElement *targetChild);

            virtual void UpdateValue();

            vec2 range;     ///< Possible range of values
            vec2 rotRange;  ///< Range of rotation of the needle element for the min and max value
            float value;    ///< Current value

        private:

    };

    /**
     * @brief Class representing a dial GUI element with a text showing the current value
     *
     * The text element (of type GuiElementTextNumber) is accesible via the textElem member
     * Font of the text element must be set, otherwise no text is drawn. Position and size should also be adjusted
     */
    class GuiElementDialText : public GuiElementDial
    {
        public:
            ///Default constructor, sets textElem to NULL
            GuiElementDialText() : textElem(NULL){}

            ///Default destructor, currenly does nothing
            virtual ~GuiElementDialText(){}

            virtual void Init();
            virtual void SetPrecision(int precision);

            GuiElementTextNumber *textElem;  ///< Element representing a text showing the current value

        protected:
            virtual GuiElement *CloneSelf();
            virtual void OnAfterChildClone(GuiElement *target, GuiElement *myChild, GuiElement *targetChild);

            virtual void UpdateValue();

        private:

    };

    /**
     * @brief Class representing a linear gauge GUI element (ie. a progressbar)
     *
     * Composed of three individual image elements:
     * baseElem - representing the base surface
     * gaugeElem - representing the moving gauge
     * coverElem - representing a transparent overlay over the gauge (think of a glass tube)
     *
     * Gauge is implemented by simply scaling the gaugeElem in X direction in range given by ScaleRange
     * Transformation of the gauge element (mainly it's position and Y scale) should be set to proper values manually
     *
     * The scale range (ScaleRange) is used to set the X scale of the gauge element when having the minimal and maximal value
     */
    class GuiElementGauge : public GuiElement
    {
        public:
            ///Default constructor, sets all elements to NULL, range to (0.0, 1.0), scaleRange to (0.0, 1.0) and value to 0.0
            GuiElementGauge() : baseElem(NULL), gaugeElem(NULL), coverElem(NULL), range(0.0, 1.0), scaleRange(0.0, 1.0), value(0.0){}

            ///Default destructor, currently does nothing
            virtual ~GuiElementGauge(){}

            virtual void Init();
            virtual void Render();

            virtual void SetValue(float value);
            virtual void SetRange(const vec2 &range);
            virtual void SetScaleRange(const vec2 &scaleRange);

            GuiElementImage *baseElem;   ///< Element representing the gauge background
            GuiElementImage *gaugeElem;  ///< Element representing the moving gauge
            GuiElementImage *coverElem;  ///< Element representing an overlay over the gauge and background

        protected:

            virtual GuiElement *CloneSelf();
            virtual void OnAfterChildClone(GuiElement *target, GuiElement *myChild, GuiElement *targetChild);

            virtual void UpdateValue();

            vec2 range;         ///< Possible range of values
            vec2 scaleRange;    ///< Range of scale of the gauge element for the min and max value
            float value;        ///< Current value


        private:

    };

    /**
     * @brief Class representing a slider GUI element
     *
     * Composed of two individual image elements:
     * baseElem - representing the base surface
     * thumbElem - representing the thumb moving over the base
     *
     * Slider is implemented by translating the thumbElem in X direction in range given by PosRange
     * Transformation of the thumbElem element (mainly it's Y position and scale) should be set to proper values manually
     *
     * The position range (PosRange) is used to set the X position of the thumb element when having the minimal and maximal value
     */
    class GuiElementSlider : public GuiElement
    {
        public:
            ///Default constructor, sets elements to NULL, range to (0.0, 1.0), posRange to (0.0, 1.0) and value to 0.0
            GuiElementSlider() : baseElem(NULL), thumbElem(NULL), range(0.0, 1.0), posRange(0.0, 1.0), value(0.0){}

            ///Default destructor, currenlty does nothing
            virtual ~GuiElementSlider(){}

            virtual void Init();
            virtual void Render();

            virtual void SetValue(float value);
            virtual void SetRange(const vec2 &range);
            virtual void SetPosRange(const vec2 &posRange);

            GuiElementImage *baseElem;   ///< Element representing the slider background
            GuiElementImage *thumbElem;  ///< Element representing the slider thumb (the moving element)

        protected:

            virtual GuiElement *CloneSelf();
            virtual void OnAfterChildClone(GuiElement *target, GuiElement *myChild, GuiElement *targetChild);

            virtual void UpdateValue();

            vec2 range;     ///< Possible range of values
            vec2 posRange;  ///< Range of position of the thumb element for the min and max value
            float value;    ///< Current value


        private:

    };

    /**
     * @brief Class representing a text input GUI element
     *
     */
    class GuiElementTextInput : public GuiElementText
    {
        public:
            ///Default contructor, sets font to NULL
            GuiElementTextInput(){};

            ///Default destructor, currently does nothing
            virtual ~GuiElementTextInput(){};

            void ProcessKey(unsigned char key);
            std::string GetText();

        protected:
            virtual GuiElement *CloneSelf();

        private:

    };
};
#endif // GUIELEMENTS_H

