#include "XcbWindowInterface.h"

#include "Window.h"
#include "XcbWindow.h"
#include "Screen.h"
#include "XcbScreen.h"
#include "XcbLayerInterface.h"

#include <xcb/xcb.h>

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

void XcbWindowInterface::Add(mt::Window* window, xcb_connection_t* connection, mt::Screen* screen, uint32_t eventMask) const
{
    if (!connection || !screen || !window)
    {
        return;
    }
    auto xcb_screen = dynamic_cast<XcbScreen*>(screen->GetPlatformScreen());

    auto hwnd = xcb_generate_id(connection);
    uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
    uint32_t values[2] = {xcb_screen->GetNativeScreen()->white_pixel, eventMask};
    xcb_create_window (connection,                    /* connection          */
                       XCB_COPY_FROM_PARENT,          /* depth               */
                       hwnd,                        /* window Id           */
                       xcb_screen->GetNativeScreen()->root,                  /* parent window       */
                       0, 0,                          /* x, y                */
                       window->GetWidth(), window->GetHeight(), /* width, height */
                       10,                            /* border_width        */
                       XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */
                       xcb_screen->GetNativeScreen()->root_visual,           /* visual              */
                       mask, values );                /* masks */

    auto xcb_window = XcbWindow::Create(hwnd);
    window->SetPlatformWindow(std::move(xcb_window));
}

void XcbWindowInterface::Show(mt::Window* window, xcb_connection_t* connection) const
{
    Map(window, connection);
}

void XcbWindowInterface::Paint(mt::Window* window, xcb_connection_t* connection, xcb_screen_t* screen, unsigned gc) const
{
    if (!window)
    {
        return;
    }
    auto xcb_window = dynamic_cast<XcbWindow*>(window->GetPlatformWindow());
    for(const auto& layer : window->GetLayers())
    {
        XcbLayerInterface::AddLayer(connection, screen, xcb_window->GetHandle(), gc, layer);
    }
}

void XcbWindowInterface::Clear(mt::Window* window, xcb_connection_t* connection) const
{
    if (!window)
    {
        return;
    }
    auto xcb_window = dynamic_cast<XcbWindow*>(window->GetPlatformWindow());
    xcb_clear_area(connection, 1, xcb_window->GetHandle(), 0, 0, window->GetWidth(), window->GetHeight());
    xcb_flush(connection);
}

void XcbWindowInterface::Map(mt::Window* window, xcb_connection_t* connection) const
{
    if (!connection or !window)
    {
        return;
    }
    auto xcb_window = dynamic_cast<XcbWindow*>(window->GetPlatformWindow());
    xcb_map_window(connection, xcb_window->GetHandle());
    xcb_flush(connection);
}
