/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus' Graphical User Interface
// Copyright (C) 2012-2019 Bruno Van de Velde (vdv_b@tgui.eu)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


#ifndef TGUI_WIDGET_HPP
#define TGUI_WIDGET_HPP

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <TGUI/Global.hpp>
#include <TGUI/Signal.hpp>
#include <TGUI/Sprite.hpp>
#include <TGUI/Layout.hpp>
#include <TGUI/Vector2f.hpp>
#include <TGUI/Loading/Theme.hpp>
#include <TGUI/Loading/DataIO.hpp>
#include <TGUI/Loading/Serializer.hpp>
#include <TGUI/Loading/Deserializer.hpp>
#include <TGUI/Renderers/WidgetRenderer.hpp>
#include <TGUI/Aurora/SmartPtr/CopiedPtr.hpp>
#include <TGUI/Aurora/Tools/Downcast.hpp>

#include <SFML/System/Time.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

#include <unordered_set>

#ifdef TGUI_USE_CPP17
    #include <any>
#else
    #include <TGUI/Any.hpp>
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace tgui
{
    class Container;

    enum class ShowAnimationType;
    namespace priv
    {
        class Animation;
    }


    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief The parent class for every widget
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API Widget : public SignalWidgetBase, public std::enable_shared_from_this<Widget>
    {
    public:

        typedef std::shared_ptr<Widget> Ptr; ///< Shared widget pointer
        typedef std::shared_ptr<const Widget> ConstPtr; ///< Shared constant widget pointer


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Default constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Widget();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Copy constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Widget(const Widget&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Widget(Widget&&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Destructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ~Widget();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Overload of copy assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Widget& operator=(const Widget&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Move assignment
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Widget& operator=(Widget&&);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets a new renderer for the widget. The renderer determines how the widget looks
        ///
        /// @param rendererData  new renderer data
        ///
        /// The renderer data is shared with this widget. When the data is changed, this widget will be updated as well.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setRenderer(std::shared_ptr<RendererData> rendererData);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer that may be shared with other widgets using the same renderer
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        WidgetRenderer* getSharedRenderer();
        const WidgetRenderer* getSharedRenderer() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer
        /// @warning After calling this function, the widget has its own copy of the renderer and it will no longer be shared.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        WidgetRenderer* getRenderer();
        const WidgetRenderer* getRenderer() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief sets the position of the widget
        ///
        /// This function completely overwrites the previous position.
        /// See the move function to apply an offset based on the previous position instead.
        /// The default position of a transformable widget is (0, 0).
        ///
        /// @param position New position
        ///
        /// @see move, getPosition
        ///
        /// Usage examples:
        /// @code
        /// // Place the widget on an exact position
        /// widget->setPosition({40, 30});
        ///
        /// // Place the widget relative to the size of its parent
        /// widget->setPosition({"5%", "10%"});
        ///
        /// // Place the widget 50 pixels below another widget and automatically move it when the other widget moves
        /// widget->setPosition({tgui::bindLeft(otherWidget), tgui::bindBottom(otherWidget) + 50});
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void setPosition(const Layout2d& position);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the position of the widget
        ///
        /// This function completely overwrites the previous position.
        /// See the move function to apply an offset based on the previous position instead.
        /// The default position of a transformable widget is (0, 0).
        ///
        /// @param x  New x coordinate
        /// @param y  New y coordinate
        ///
        /// @warning This setPosition overload must never be used from internal TGUI code or by custom widget implementations
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setPosition(Layout x, Layout y)
        {
            setPosition({std::move(x), std::move(y)});
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Gets the position of the widget
        ///
        /// @return Current position
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Vector2f getPosition() const
        {
            return m_position.getValue();
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the size of the widget
        ///
        /// @param size  Size of the widget
        ///
        /// Usage examples:
        /// @code
        /// // Give the widget an exact size
        /// widget->setSize({40, 30});
        ///
        /// // Give the widget a size relative to the size of its parent
        /// widget->setSize({"20%", "5%"});
        ///
        /// // Make the widget 50 pixels higher than some other widget and automatically resize it when the other widget resizes
        /// widget->setSize({200, tgui::bindHeight(otherWidget) + 50});
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void setSize(const Layout2d& size);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the size of the widget
        ///
        /// @param width   Width of the widget
        /// @param height  Height of the widget
        ///
        /// @warning This setSize overload must never be used from internal TGUI code or by custom widget implementations
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSize(Layout width, Layout height)
        {
            setSize({std::move(width), std::move(height)});
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the size of the widget
        ///
        /// @return Size of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Vector2f getSize() const
        {
            return m_size.getValue();
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the entire size that the widget is using
        ///
        /// This function will return a value equal or greater than what getSize returns.
        /// If the widget would e.g. have borders around it then this function will return the size, including these borders.
        ///
        /// @return Full size of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual Vector2f getFullSize() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Get the absolute position of the widget instead of the relative position to its parent
        ///
        /// @return Absolute position of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual Vector2f getAbsolutePosition() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the distance between the position where the widget is drawn and where the widget is placed
        ///
        /// The offset is (0,0) for almost all widgets.
        ///
        /// @return Offset of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual Vector2f getWidgetOffset() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Shows the widget by introducing it with an animation
        ///
        /// The animation will also be played if the widget was already visible.
        ///
        /// During the animation the position, size and/or opacity may change. Once the animation is done the widget will
        /// be back in the state in which it was when this function was called.
        ///
        /// @param type     Type of the animation
        /// @param duration Duration of the animation
        ///
        /// @see hideWithEffect
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef TGUI_NEXT
        void showWithEffect(ShowAnimationType type, sf::Time duration);
#else
        virtual void showWithEffect(ShowAnimationType type, sf::Time duration);
#endif

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Hides the widget by making it leave with an animation
        ///
        /// If the widget is already hidden then the animation will still play but you will not see it.
        ///
        /// During the animation the position, size and/or opacity may change. Once the animation is done the widget will
        /// be back in the state in which it was when this function was called, except that it will no longer be visible.
        ///
        /// @param type     Type of the animation
        /// @param duration Duration of the animation
        ///
        /// @see showWithEffect
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef TGUI_NEXT
        void hideWithEffect(ShowAnimationType type, sf::Time duration);
#else
        virtual void hideWithEffect(ShowAnimationType type, sf::Time duration);
#endif


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Shows or hides a widget
        /// @param visible  Is the widget visible?
        ///
        /// The widget won't receive events (and thus won't send callbacks) nor will it be drawn when hidden.
        /// The widget is visible by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void setVisible(bool visible);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns true when the widget is visible
        ///
        /// @return Is the widget visible?
        ///
        /// If this function returns false then the widget is hidden, which means that it won't receive events (and thus won't
        /// send callbacks) and it won't be drawn. All widgets are visible by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool isVisible() const
        {
            return m_visible;
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Enables or disables the widget
        /// @param enabled  Is the widget enabled?
        ///
        /// The disabled widget will no longer receive events and thus no longer send callbacks.
        /// All widgets are enabled by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void setEnabled(bool enabled);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns true when the widget is enabled
        ///
        /// @return Is the widget enabled?
        ///
        /// If this function returns false then the widget is disabled and will longer receive events and it will thus no longer send callbacks.
        /// All widgets are enabled by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool isEnabled() const
        {
            return m_enabled;
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Focus or unfocus the widget
        /// @param focused  Is the widget focused?
        ///
        /// When a widget is focused, the previously focused widget will be unfocused.
        ///
        /// @warning This function only works properly when the widget was already added to its parent (e.g. the Gui).
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void setFocused(bool focused);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns true when the widget is focused and false otherwise
        ///
        /// @return Is the widget focused?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool isFocused() const
        {
            return m_focused;
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the type of the widget
        ///
        /// @return Type of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const std::string& getWidgetType() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns a pointer to the parent widget
        ///
        /// @return Pointer to the parent
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Container* getParent() const
        {
            return m_parent;
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Places the widget before all other widgets
        ///
        /// @warning This function only has an effect when the widget was already added to its parent (e.g. the Gui).
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef TGUI_NEXT
        void moveToFront();
#else
        virtual void moveToFront();
#endif

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Places the widget behind all other widgets
        ///
        /// @warning This function only has an effect when the widget was already added to its parent (e.g. the Gui).
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef TGUI_NEXT
        void moveToBack();
#else
        virtual void moveToBack();
#endif

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Stores some data into the widget
        /// @param userData  Data to store
        ///
        /// Examples:
        /// @code
        /// widget->setUserData("Data");
        /// widget->setUserData(5);
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    #ifdef TGUI_USE_CPP17
        void setUserData(std::any userData)
    #else
        void setUserData(tgui::Any userData)
    #endif
        {
            m_userData = std::move(userData);
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns data stored in the widget
        /// @return Stored data
        /// @throw std::bad_cast if the template type does not match the type inside the std::any variable passed in setUserData.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        template <typename T>
        T getUserData() const
        {
        #ifdef TGUI_USE_CPP17
            return std::any_cast<T>(m_userData);
        #else
            return m_userData.as<T>();
        #endif
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the font of the widget that is used when no font is set in the renderer
        ///
        /// @param font  New font for the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setInheritedFont(const Font& font);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the font of the widget that is used when no font is set in the renderer
        ///
        /// @return Font of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const Font& getInheritedFont() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the opacity of the widget that will be multiplied with the opacity set in the renderer
        ///
        /// @param opacity  Opacity of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setInheritedOpacity(float opacity);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the opacity of the widget that is multiplied with the opacity set in the renderer
        ///
        /// @return Opacity of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        float getInheritedOpacity() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the tool tip that should be displayed when hovering over the widget
        ///
        /// @param toolTip  Any widget that you want to use as a tool tip (usually a Label)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setToolTip(Widget::Ptr toolTip);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the tool tip that is displayed when hovering over the widget
        ///
        /// @return The widget that is used as tool tip or nullptr when no tool tip has been set
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Widget::Ptr getToolTip() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the widget can gain focus
        /// @return Can the widget be focused?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool canGainFocus() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the widget is a container widget or not
        /// @return Does the widget inherit from the Container class, giving it the ability to have child widgets?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool isContainer() const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// This function is called when the widget is added to a container.
        /// You should not call this function yourself.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void setParent(Container* parent);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// This function is called every frame with the time passed since the last frame.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void update(sf::Time elapsedTime);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the mouse position (which is relative to the parent widget) lies on top of the widget
        /// @return Is the mouse on top of the widget?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool mouseOnWidget(Vector2f pos) const = 0;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void leftMousePressed(Vector2f pos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void leftMouseReleased(Vector2f pos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void rightMousePressed(Vector2f pos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void rightMouseReleased(Vector2f pos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void mouseMoved(Vector2f pos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void keyPressed(const sf::Event::KeyEvent& event);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void textEntered(std::uint32_t key);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// Returns whether the scrolling was handled by the widget or not.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool mouseWheelScrolled(float delta, Vector2f pos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void mouseNoLongerOnWidget();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void mouseNoLongerDown();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        // Show the tool tip when the widget is located below the mouse.
        // Returns its tool tip or the tool tip from a child widget if the mouse is on top of the widget.
        // A nullptr is returned when the mouse is not on top of the widget or when the tool tip is empty.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual Widget::Ptr askToolTip(Vector2f mousePos);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// @brief Returns the layout object that is being used for the position
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const Layout2d& getPositionLayout() const
        {
            return m_position;
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// @brief Returns the layout object that is being used for the size
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const Layout2d& getSizeLayout() const
        {
            return m_size;
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// Requests the widget to inform the layout about position changes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void bindPositionLayout(Layout* layout);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// Informs the widget that the layout no longer needs to know about position changes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void unbindPositionLayout(Layout* layout);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// Requests the widget to inform the layout about position changes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void bindSizeLayout(Layout* layout);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// Informs the widget that the layout no longer needs to know about position changes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void unbindSizeLayout(Layout* layout);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Draw the widget to a render target
        ///
        /// This is a pure virtual function that has to be implemented by the derived class to define how the widget is drawn.
        ///
        /// @param target Render target to draw to
        /// @param states Current render states
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Downcast const widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        template <typename WidgetType>
        std::shared_ptr<const WidgetType> cast() const
        {
            return std::dynamic_pointer_cast<const WidgetType>(shared_from_this());
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Downcast widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        template <typename WidgetType>
        std::shared_ptr<WidgetType> cast()
        {
            return std::dynamic_pointer_cast<WidgetType>(shared_from_this());
        }


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Makes a copy of the widget if you don't know its exact type
        ///
        /// This function should only be used when you don't know the type of the widget.
        /// If you know what kind of widget you are copying, you should use the copy function.
        ///
        /// @return Copy of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual Widget::Ptr clone() const = 0;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        using SavingRenderersMap = std::map<const Widget*, std::pair<std::unique_ptr<DataIO::Node>, std::string>>;
        using LoadingRenderersMap = std::map<std::string, std::shared_ptr<RendererData>>;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Retrieves a signal based on its name
        ///
        /// @param signalName  Name of the signal
        ///
        /// @return Signal that corresponds to the name
        ///
        /// @throw Exception when the name does not match any signal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Signal& getSignal(std::string signalName) override;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Function called when one of the properties of the renderer is changed
        ///
        /// @param property  Lowercase name of the property that was changed
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void rendererChanged(const std::string& property);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Saves the widget as a tree node in order to save it to a file
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual std::unique_ptr<DataIO::Node> save(SavingRenderersMap& renderers) const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Loads the widget from a tree of nodes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void load(const std::unique_ptr<DataIO::Node>& node, const LoadingRenderersMap& renderers);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function is called when the mouse enters the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void mouseEnteredWidget();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function is called when the mouse leaves the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void mouseLeftWidget();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Draw a rectangle shape with the right opacity
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void drawRectangleShape(sf::RenderTarget& target,
                                const sf::RenderStates& states,
                                Vector2f size,
                                Color color) const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Draw the borders around the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void drawBorders(sf::RenderTarget& target,
                         const sf::RenderStates& states,
                         const Borders& borders,
                         Vector2f size,
                         Color color) const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    private:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Callback function which is called on a renderer change and which calls the virtual rendererChanged function
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void rendererChangedCallback(const std::string& property);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public:

        SignalVector2f  onPositionChange = {"PositionChanged"};         ///< The position of the widget changed. Optional parameter: new position
        SignalVector2f  onSizeChange     = {"SizeChanged"};             ///< The size of the widget changed. Optional parameter: new size
        Signal          onFocus          = {"Focused"};                 ///< The widget was focused
        Signal          onUnfocus        = {"Unfocused"};               ///< The widget was unfocused
        Signal          onMouseEnter     = {"MouseEntered"};            ///< The mouse entered the widget
        Signal          onMouseLeave     = {"MouseLeft"};               ///< The mouse left the widget
        SignalAnimation onAnimationFinished = {"AnimationFinished"};    ///< A show or hide animation finished. Optional parameters: animation type, new widget visibility or both


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        std::string m_type;

        Layout2d m_position;
        Layout2d m_size;

        // The previous position and size have to be stored because when setPosition/setSize is called, the layout may already be
        // changed and there would be no way for the widget to detect whether the values changed or not.
        Vector2f m_prevPosition;
        Vector2f m_prevSize;

        // Layouts that need to recalculate their value when the position or size of this widget changes
        std::unordered_set<Layout*> m_boundPositionLayouts;
        std::unordered_set<Layout*> m_boundSizeLayouts;

        // When a widget is disabled, it will no longer receive events
        bool m_enabled = true;

        // Is the widget visible? When it is invisible it will not receive events and it won't be drawn.
        bool m_visible = true;

        // This will point to our parent widget. If there is no parent then this will be nullptr.
        Container* m_parent = nullptr;

        // Is the mouse on top of the widget? Did the mouse go down on the widget?
        bool m_mouseHover = false;
        bool m_mouseDown = false;

        // Is the widget focused?
        bool m_focused = false;

        // Keep track of the elapsed time.
        sf::Time m_animationTimeElapsed;

        // This is set to true for widgets that have something to be dragged around (e.g. sliders and scrollbars)
        bool m_draggableWidget = false;

        // This is set to true for widgets that store other widgets inside them
        bool m_containerWidget = false;

        // The tool tip connected to the widget
        Widget::Ptr m_toolTip = nullptr;

        // Renderer of the widget
        aurora::CopiedPtr<WidgetRenderer> m_renderer = aurora::makeCopied<WidgetRenderer>();

        // Show animations
        std::vector<std::shared_ptr<priv::Animation>> m_showAnimations;

        // Renderer properties that can be passed from containers to their children
        Font m_inheritedFont;
        float m_inheritedOpacity = 1;

        // Cached renderer properties
        Font  m_fontCached = getGlobalFont();
        float m_opacityCached = 1;
        bool m_transparentTextureCached = false;

    #ifdef TGUI_USE_CPP17
        std::any m_userData;
    #else
        tgui::Any m_userData;
    #endif

        std::function<void(const std::string& property)> m_rendererChangedCallback = [this](const std::string& property){ rendererChangedCallback(property); };


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        friend class Container; // Container accesses m_mouseDown, m_draggableWidget, save and load
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif // TGUI_WIDGET_HPP
