#pragma once

#include "DiscretePoint.h"

#include <memory>
#include <vector>

class MouseEvent;
class KeyboardEvent;
class PaintEvent;

class VisualLayer;
class Color;

class Widget
{
public:
    struct BoundaryOffset{
        unsigned mLeft = 0;
        unsigned mRight = 0;
        unsigned mTop = 0;
        unsigned mBottom = 0;
    };

    struct BoundedSize{
        unsigned mWidth = 0;
        unsigned mMaxWidth = 0;
        unsigned mMinWidth = 0;
        unsigned mHeight = 0;
        unsigned mMaxHeight = 0;
        unsigned mMinHeight = 0;
    };

protected:
    DiscretePoint mLocation;
    BoundedSize mSize;
    BoundaryOffset mPadding;
    BoundaryOffset mMargin;
    std::vector<std::unique_ptr<VisualLayer> > mMyLayers;
    std::vector<VisualLayer*> mLayers;
    std::vector<std::unique_ptr<Widget> > mChildren;
    unsigned mBorderThickness;
    std::unique_ptr<Color> mBackgroundColor;
    std::unique_ptr<Color> mBorderColor;
    bool mVisible;

public:

    Widget();

    virtual ~Widget();

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

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

    BoundedSize GetSize() const;

    std::vector<VisualLayer*> GetLayers() 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(std::unique_ptr<Color> color);

    void SetBounds(unsigned width, unsigned height);

    void SetSize(const BoundedSize& size);

    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);

protected:

    virtual bool OnMyKeyboardEvent(const KeyboardEvent* event);

    virtual void OnMyMouseEvent(const MouseEvent* event);

    virtual void AddChildLayers(const PaintEvent* event);

    virtual void AddBackground(const PaintEvent* event);

    void CopyMyLayers();
};

using WidgetUPtr = std::unique_ptr<Widget>;
