#pragma once

#include "FontItem.h"
#include "WidgetState.h"
#include "BoxGeometry.h"
#include "TransformNode.h"

#include "ITheme.h"
#include "ElevationTokens.h"

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

class MouseEvent;
class KeyboardEvent;
class PaintEvent;
class FocusEvent;

class AbstractVisualNode;
class TransformNode;
class RectangleNode;

namespace mt
{
class Window;
}

class Widget : public BoxGeometry
{
public:
    Widget();

    virtual ~Widget();

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

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

    TransformNode* getRootNode() const;

    const std::string& getName() const;

    mt::Window* getTopLevelWindow() const;

    bool needsUpdate() const;

    virtual void onPaintEvent(const PaintEvent* event);

    virtual bool onMouseEvent(const MouseEvent* event);

    virtual bool onKeyboardEvent(const KeyboardEvent* event);

    virtual bool onFocusInEvent(const FocusEvent* event);

    void setBackgroundOpacity(float opacity);

    void setBackground(Theme::Sys::Color token);

    void setBackgroundTone(Theme::Sys::Color token);

    void setElevation(Theme::Sys::Elevation elevation);

    void setOutlineThickness(double thickness);

    void setOutline(Theme::Sys::Color token);

    void setRadius(double radius);

    void setVisible(bool visible);

    void setName(const std::string& name);

    void setWindow(mt::Window* window);

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

    virtual void onMyMouseEvent(const MouseEvent* event);

    virtual void createOrUpdateGeometry();

    virtual void updateMaterial(const PaintEvent* event);

    virtual void updateTransform();

    virtual void updateState();

    virtual void updateBackground(const PaintEvent* event);

    virtual void doPaint(const PaintEvent* event);

    virtual void updateChildLocations();

    virtual bool isDirty() const;

    void setFocus(bool hasFocus);

    void setParent(Widget* parent);

    Widget* getParent() const;

    mt::Window* getWindow() const;

    std::unique_ptr<TransformNode> mRootNode;
    std::vector<std::unique_ptr<Widget> > mChildren;

    Theme::Sys::Color mBorder;
    double mBorderThickness{0};

    Theme::Sys::Color mBackgroundTone;
    Theme::Sys::Color mBackground;
    Theme::Sys::Elevation mElevation{ Theme::Sys::Elevation::Level_0 };
    float mBackgroundOpacity{ 1.0 };

    bool mVisible{true};
    double mRadius{ 0.0 };

    std::unique_ptr<RectangleNode> mBackgroundNode;

    bool mStateDirty{ true };
    bool mMaterialDirty{true};
    bool mVisibilityDirty{true};
    bool mGeometryDirty{ true };

    bool mAcceptsFocus{ false };
    bool mHasFocus{ false };

    bool mContainsMouse{ false };

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

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

using WidgetUPtr = std::unique_ptr<Widget>;
