#include "WaylandSurface.h"

#include "WaylandEglWindowInterface.h"
#include "ImagePrimitives.h"

#include "DrawingContext.h"
#include "FontsManager.h"

void WaylandSurface::add(mt::Window* window, wl_compositor* compositor, xdg_wm_base* xdg_wm_base, std::shared_ptr<WaylandBuffer> buffer, FontsManager* fontsManager, WaylandEglInterface* eglInterface)
{
    auto wayland_window = std::make_unique<WaylandSurface>(window, compositor, xdg_wm_base, buffer, eglInterface);

    const auto drawing_mode = eglInterface ? DrawingMode::GRAPH : DrawingMode::RASTER;
    window->setPlatformWindow(std::move(wayland_window), fontsManager, drawing_mode);
}

WaylandSurface::WaylandSurface(mt::Window* window, wl_compositor* compositor, xdg_wm_base* xdg_wm_base, std::shared_ptr<WaylandBuffer> buffer, WaylandEglInterface* eglInterface)
    : IPlatformWindow(window),
      mCompositor(compositor),
      mXdgWmBase(xdg_wm_base),
      mBuffer(buffer)
{
    if (eglInterface)
    {
        mEglWindowInterface = std::make_unique<WaylandEglWindowInterface>(eglInterface);
    }
}

WaylandSurface::~WaylandSurface()
{

}

void WaylandSurface::show()
{
    map();
}

void WaylandSurface::clear()
{

}

void WaylandSurface::map()
{
    if (!mSurface)
    {
        initialize();
    }
}

void WaylandSurface::initialize()
{
    mSurface = wl_compositor_create_surface(mCompositor);
    mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgWmBase, mSurface);

    auto xdg_surface_configure = [](void *data, struct xdg_surface *xdg_surface, uint32_t serial)
    {
        auto thisClass = static_cast<WaylandSurface*>(data);
        thisClass->onConfigure(xdg_surface, serial);
    };
    mXdgSurfaceListener.configure = xdg_surface_configure;

    xdg_surface_add_listener(mXdgSurface, &mXdgSurfaceListener, this);

    mXdgTopLevel = xdg_surface_get_toplevel(mXdgSurface);
    xdg_toplevel_set_title(mXdgTopLevel, "Example client");

    auto region = wl_compositor_create_region(mCompositor);
    wl_region_add(region, 0, 0, mWindow->getWidth(), mWindow->getHeight());
    wl_surface_set_opaque_region(mSurface, region);

    wl_surface_commit(mSurface);
}

void WaylandSurface::onConfigure(xdg_surface *xdg_surface, uint32_t serial)
{
    xdg_surface_ack_configure(xdg_surface, serial);
    beforePaint(nullptr);
}

void WaylandSurface::beforePaint(mt::Screen* screen)
{
    if (mEglWindowInterface)
    {
        mEglWindowInterface->initialize(mSurface, mWindow->getWidth(), mWindow->getHeight());
        mEglWindowInterface->beforePaint();
    }
    else
    {
        beforePaintSoftware();
    }
}

void WaylandSurface::afterPaint(mt::Screen* screen)
{
    if (mEglWindowInterface)
    {
        mEglWindowInterface->afterPaint();
    }
    else
    {
        afterPaintSoftware();
    }
}

unsigned WaylandSurface::getImageBufferSize() const
{
    const auto width = mWindow->getWidth();
    const auto height = mWindow->getHeight();
    const int stride = width * mWorkingBitDepth;

    //const int numBuffers = 2; // i.e. front/back
    return height * stride * mNumFrameBuffers;
}

void WaylandSurface::beforePaintSoftware()
{
    const auto width = mWindow->getWidth();
    const auto height = mWindow->getHeight();
    const int stride = width * mWorkingBitDepth;

    const int shm_pool_size = getImageBufferSize();

    mBuffer->initializeSharedBuffer(shm_pool_size);
    mBuffer->setUpPool(shm_pool_size, width, height, stride);

    //ImagePrimitives::drawCheckerboard(mBuffer->getPoolData(), width, height, offset);
}

void WaylandSurface::afterPaintSoftware()
{
    const int shm_pool_size = getImageBufferSize();
    mBuffer->tearDownPool(shm_pool_size);

    wl_surface_attach(mSurface, mBuffer->getWorkingBuffer(), 0, 0);
    //wl_surface_damage(mSurface, 0, 0, UINT32_MAX, UINT32_MAX);
    wl_surface_commit(mSurface);
}

