#include "Widget.h"

#include "RectangleElement.h"
#include "MouseEvent.h"
#include "KeyboardEvent.h"
#include "PaintEvent.h"
#include "VisualLayer.h"
#include "TextElement.h"
#include "Color.h"

#include <algorithm>
#include <iterator>

Widget::Widget()
    : mLocation(DiscretePoint(0, 0)),
      mSize({100, 0, 0, 100, 0, 0}),
      mPadding(),
      mMargin(),
      mMyLayers(),
      mLayers(),
      mChildren(),
      mBorderThickness(0),
      mBackgroundColor(Color::Create(255, 255, 255)),
      mBorderColor(Color::Create(0, 0, 0)),
      mVisible(true)
{

}

Widget::~Widget()
{

}

std::unique_ptr<Widget> Widget::Create()
{
    return std::make_unique<Widget>();
}

void Widget::AddWidget(WidgetUPtr widget)
{
    mChildren.push_back(std::move(widget));
}

Widget::BoundedSize Widget::GetSize() const
{
    return mSize;
}

DiscretePoint Widget::GetLocation() const
{
    return mLocation;
}

std::vector<VisualLayer*> Widget::GetLayers() const
{
    return mLayers;
}

void Widget::SetSize(const BoundedSize& size)
{
    mSize = size;
}

void Widget::SetMargin(unsigned margin)
{
    mMargin = {margin, margin, margin, margin};
}

void Widget::SetMargin(const BoundaryOffset& margin)
{
    mMargin = margin;
}

void Widget::SetPadding(unsigned padding)
{
    mPadding = {padding, padding, padding, padding};
}

void Widget::SetPadding(const BoundaryOffset& padding)
{
    mPadding = padding;
}

void Widget::CopyMyLayers()
{
    mLayers.clear();
    mLayers.reserve(mMyLayers.size());
    auto getRaw = [](const VisualLayerUPtr& uptr){return uptr.get();};
    std::transform(mMyLayers.begin(), mMyLayers.end(), std::back_inserter(mLayers), getRaw);
}

void Widget::SetBounds(unsigned width, unsigned height)
{
    mSize.mWidth = width;
    mSize.mHeight = height;
}

void Widget::AddChildLayers(const PaintEvent* event)
{
    for(auto& child: mChildren)
    {
        child->SetBounds(mSize.mWidth, mSize.mHeight);
        child->SetLocation(mLocation);
        child->OnPaintEvent(event);
        const auto layers = child->GetLayers();
        mLayers.insert(mLayers.end(), layers.begin(), layers.end());
    }
}

void Widget::OnPaintEvent(const PaintEvent* event)
{
    mMyLayers.clear();
    mLayers.clear();

    if(!mVisible)return;
    AddBackground(event);
    CopyMyLayers();
    AddChildLayers(event);
}

bool Widget::Contains(const DiscretePoint& loc) const
{
    if(loc.GetX() < mLocation.GetX()) return false;
    if(loc.GetX() > mLocation.GetX() + mSize.mWidth) return false;
    if(loc.GetY() > mLocation.GetY() + mSize.mHeight) return false;
    if(loc.GetY() < mLocation.GetY()) return false;
    return true;
}

bool Widget::OnKeyboardEvent(const KeyboardEvent* event)
{
    bool inChild = false;
    for(const auto& child : mChildren)
    {
        if(child->OnKeyboardEvent(event))
        {
            inChild = true;
            break;
        }
    }
    if(!inChild)
    {
        return OnMyKeyboardEvent(event);
    }
    return true;
}

bool Widget::OnMyKeyboardEvent(const KeyboardEvent* event)
{
    return false;
}

bool Widget::OnMouseEvent(const MouseEvent* event)
{
    bool inChild = false;
    for(const auto& child : mChildren)
    {
        if(child->OnMouseEvent(event))
        {
            inChild = true;
            break;
        }
    }
    if(!inChild)
    {
        if(Contains(event->GetClientLocation()))
        {
            OnMyMouseEvent(event);
            return true;
        }
        else
        {
            return false;
        }
    }
    return true;
}

void Widget::OnMyMouseEvent(const MouseEvent* event)
{

}

void Widget::AddBackground(const PaintEvent* event)
{
    unsigned locX = mLocation.GetX() + mMargin.mLeft;
    unsigned locY = mLocation.GetY() + mMargin.mTop;
    unsigned deltaX = mSize.mWidth - mMargin.mLeft - mMargin.mRight;
    unsigned deltaY = mSize.mHeight - mMargin.mTop - mMargin.mBottom;
    auto shape = RectangleElement::Create(DiscretePoint(locX, locY),
            deltaX, deltaY);
    shape->SetFillColor(Color::Create(*mBackgroundColor));
    auto shapeLayer = VisualLayer::Create();
    shapeLayer->SetShape(std::move(shape));
    mMyLayers.push_back(std::move(shapeLayer));
}

void Widget::SetBackgroundColor(ColorUPtr color)
{
    mBackgroundColor = std::move(color);
}

void Widget::SetLocation(const DiscretePoint& loc)
{
    mLocation = loc;
}

void Widget::SetVisible(bool visible)
{
    mVisible = visible;
}
