#define _GNU_SOURCE

#include "u.h"
#include "lib.h"
#include "dat.h"
#include "fns.h"
#include "user.h"
#include <draw.h>
#include <memdraw.h>

#undef up

#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <linux/input-event-codes.h>
#include <xkbcommon/xkbcommon.h>
#include "xdg-shell-protocol.h"

#include "screen.h"
#include "wl-inc.h"

static Wlwin *gwin;

Memimage *gscreen;

static Wlwin*
newwlwin(void)
{
	Wlwin *wl;

	wl = mallocz(sizeof *wl, 1);
	if(wl == nil)
		panic("malloc Wlwin");
	wl->dx = 1024;
	wl->dy = 1024;
	wl->monx = wl->dx;
	wl->mony = wl->dy;
	return wl;
}

void
wlclose(Wlwin *wl)
{
	wl->runing = 0;
	exits(nil);
}

void
wltogglemaximize(Wlwin *wl)
{
	if(wl->maximized)
		xdg_toplevel_unset_maximized(wl->xdg_toplevel);
	else
		xdg_toplevel_set_maximized(wl->xdg_toplevel);
}

void
wlminimize(Wlwin *wl)
{
	xdg_toplevel_set_minimized(wl->xdg_toplevel);
}

void
wlmove(Wlwin *wl, uint32_t serial)
{
	xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial);
}

void
wlmenu(Wlwin *wl, uint32_t serial)
{
	xdg_toplevel_show_window_menu(wl->xdg_toplevel, wl->seat, serial, wl->mouse.xy.x, wl->mouse.xy.y);
}

static void
wlupdatecsdrects(Wlwin *wl)
{
	Point offset;
	Rectangle button;

	if(!wl->client_side_deco){
		memset(&wl->csd_rects, 0, sizeof wl->csd_rects);
		return;
	}

	wl->csd_rects.bar = Rect(0, 0, wl->dx, csd_bar_height);

	offset = Pt(csd_button_width + 4, 0);
	button = Rect(0, 4, csd_button_width, csd_button_width + 4);
	button = rectsubpt(button, offset);

	wl->csd_rects.button_close = button = rectaddpt(button, Pt(wl->dx, 0));
	wl->csd_rects.button_maximize = button = rectsubpt(button, offset);
	wl->csd_rects.button_minimize = rectsubpt(button, offset);
}

static void
wlfillrect(Wlwin *wl, Rectangle rect, uint32_t color)
{
	Point p;
	uint32_t *data = wl->shm_data;

	for(p.y = rect.min.y; p.y < rect.max.y; p.y++)
		for(p.x = rect.min.x; p.x < rect.max.x; p.x++)
			data[p.y * wl->dx + p.x] = color;
}

static void
wldrawcsd(Wlwin *wl)
{
	if(!wl->client_side_deco)
		return;
	wlfillrect(wl, wl->csd_rects.bar, 0xAAAAAA);
	wlfillrect(wl, wl->csd_rects.button_close, DRed >> 8);
	wlfillrect(wl, wl->csd_rects.button_maximize, DGreen >> 8);
	wlfillrect(wl, wl->csd_rects.button_minimize, DYellow >> 8);
}

void
wlflush(Wlwin *wl)
{
	Point p;

	wl_surface_attach(wl->surface, wl->screenbuffer, 0, 0);
	if(wl->dirty){
		p.x = wl->r.min.x;
		for(p.y = wl->r.min.y; p.y < wl->r.max.y; p.y++)
			memcpy(wl->shm_data+(p.y*wl->dx+p.x)*4, byteaddr(gscreen, p), Dx(wl->r)*4);
		wl_surface_damage(wl->surface, p.x, wl->r.min.y, Dx(wl->r), Dy(wl->r));
		wl->dirty = 0;
	}
	wl_surface_commit(wl->surface);
}

void  _screenresize(Rectangle);

void
wlresize(Wlwin *wl, int x, int y)
{
	Rectangle r;

	wl->dx = x;
	wl->dy = y;
	wlupdatecsdrects(wl);

	qlock(&drawlock);
	wlallocbuffer(wl);
	r = Rect(0, wl->csd_rects.bar.max.y, wl->dx, wl->dy);
	if(gscreen != nil)
		freememimage(gscreen);
	gscreen = allocmemimage(r, XRGB32);
	gscreen->clipr = ZR;
	qunlock(&drawlock);

	screenresize(r);

	qlock(&drawlock);
	wl->dirty = 1;
	wl->r = r;
	wldrawcsd(wl);
	wlflush(wl);
	qunlock(&drawlock);
}

void
dispatchproc(void *a)
{
	Wlwin *wl;
	wl = a;
	while(wl->runing)
		wl_display_dispatch(wl->display);
}

static Wlwin*
wlattach(char *label)
{
	Rectangle r;
	Wlwin *wl;

	wl = newwlwin();
	gwin = wl;
	wl->display = wl_display_connect(nil);
	if(wl->display == nil)
		panic("could not connect to display");

	memimageinit();
	wlsetcb(wl);
	wlupdatecsdrects(wl);
	wlflush(wl);
	wlsettitle(wl, label);

	r = Rect(0, wl->csd_rects.bar.max.y, wl->dx, wl->dy);
	gscreen = allocmemimage(r, XRGB32);
	gscreen->clipr = r;

	wl->runing = 1;
	kproc("wldispatch", dispatchproc, wl);
	qlock(&drawlock);
	wldrawcsd(wl);
	terminit();
	wlflush(wl);
	qunlock(&drawlock);
	return wl;
}

void
screeninit(void)
{
	wlattach("drawterm");
}

void
guimain(void)
{
	cpubody();
}

Memdata*
attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
{
	*r = gscreen->clipr;
	*chan = gscreen->chan;
	*depth = gscreen->depth;
	*width = gscreen->width;
	*softscreen = 1;

	gscreen->data->ref++;
	return gscreen->data;
}

void
flushmemscreen(Rectangle r)
{
	gwin->dirty = 1;
	gwin->r = r;
	wlflush(gwin);
}

void
screensize(Rectangle r, ulong chan)
{
	flushmemscreen(r);
}

void
setcursor(void)
{
	qlock(&drawlock);
	wldrawcursor(gwin, &cursor);
	qunlock(&drawlock);
}

void
mouseset(Point p)
{
	wlsetmouse(gwin, p);
}

char*
clipread(void)
{
	return wlgetsnarf(gwin);
}

int
clipwrite(char *data)
{
	wlsetsnarf(gwin, data);
	return strlen(data);
}

void
getcolor(ulong i, ulong *r, ulong *g, ulong *b)
{
}

void
setcolor(ulong index, ulong red, ulong green, ulong blue)
{
}
