#pragma once

#include "DiscretePoint.h"
#include "FontItem.h"
#include "Color.h"

#include <memory>
#include <vector>
#include <string>

class MouseEvent;
class KeyboardEvent;
class PaintEvent;

class AbstractVisualNode;
class TransformNode;
class RectangleNode;

namespace mt
{
class Window;
}

class Widget
{
public:
    struct BoundaryOffset
    {
        bool operator==(const BoundaryOffset& rhs) const
        {
           return (mLeft == rhs.mLeft)
           && (mRight == rhs.mRight)
           && (mTop == rhs.mTop)
           && (mBottom == rhs.mBottom);
        }
        bool operator!=(const BoundaryOffset& rhs) const
        {
          return !operator==(rhs);
        }

        unsigned mLeft = 0;
        unsigned mRight = 0;
        unsigned mTop = 0;
        unsigned mBottom = 0;
    };

    struct BoundedSize
    {
        bool operator==(const BoundedSize& rhs) const
        {
           return (mWidth == rhs.mWidth)
           && (mMaxWidth == rhs.mMaxWidth)
           && (mMinWidth == rhs.mMinWidth)
           && (mHeight == rhs.mHeight)
           && (mMaxHeight == rhs.mMaxHeight)
           && (mMinHeight == rhs.mMinHeight);
        }
        bool operator!=(const BoundedSize& rhs) const
        {
          return !operator==(rhs);
        }

        unsigned mWidth = 0;
        unsigned mMaxWidth = 0;
        unsigned mMinWidth = 0;
        unsigned mHeight = 0;
        unsigned mMaxHeight = 0;
        unsigned mMinHeight = 0;
    };

public:

    Widget();

    virtual ~Widget();

    static std::unique_ptr<Widget> Create();

    virtual void addWidget(std::unique_ptr<Widget> widget);

    BoundedSize getSize() const;

    TransformNode* getRootNode() const;

    const DiscretePoint& getLocation() const;

    virtual void onPaintEvent(const PaintEvent* event);

    virtual bool onMouseEvent(const MouseEvent* event);

    virtual bool onKeyboardEvent(const KeyboardEvent* event);

    bool contains(const DiscretePoint& loc) const;

    void setBackgroundColor(const Color& color);

    void setBounds(unsigned width, unsigned height);

    void setSize(const BoundedSize& size);

    void setMaxWidth(unsigned maxWidth);

    void setMargin(unsigned margin);

    void setMargin(const BoundaryOffset& margin);

    void setPadding(unsigned padding);

    void setPadding(const BoundaryOffset& padding);

    void setLocation(const DiscretePoint& loc);

    void setVisible(bool visible);

    void setName(const std::string& name)
    {
        mName = name;
    }

    const std::string& getName() const
    {
        return mName;
    }

    bool needsUpdate() const;

    void setWindow(mt::Window* window);

    mt::Window* getTopLevelWindow() const;

protected:
    virtual bool onMyKeyboardEvent(const KeyboardEvent* event);

    virtual void onMyMouseEvent(const MouseEvent* event);

    virtual void updateBackground(const PaintEvent* event);

    virtual void doPaint(const PaintEvent* event);

    virtual void updateChildLocations();

    virtual bool isDirty() const;



    void setParent(Widget* parent);

    Widget* getParent() const;

    mt::Window* getWindow() const;

    DiscretePoint mLocation;
    BoundedSize mSize;
    BoundaryOffset mPadding;
    BoundaryOffset mMargin;

    std::unique_ptr<TransformNode> mRootNode;
    std::vector<std::unique_ptr<Widget> > mChildren;
    unsigned mBorderThickness{0};
    Color mBackgroundColor;
    Color mBorderColor;
    bool mVisible{true};

    std::unique_ptr<RectangleNode> mBackgroundNode;

    bool mTransformDirty{true};
    bool mMaterialDirty{true};
    bool mVisibilityDirty{true};

    std::string mName;
    std::vector<TransformNode*> mPendingChildNodes;

    FontItem mDefaultFont;
    Widget* mParent{nullptr};
    mt::Window* mWindow{nullptr};
};

using WidgetUPtr = std::unique_ptr<Widget>;
