#include <../arch/arch.h>

IMPLEMENT_DEQUE_API(event,V_CALL,ui_event_t)

char_p	V_GLOBAL
uiKeyscan2Name(unsigned scan)
{
	char *name;

#define	MATCH_SCAN(scan_name) \
	case sc_##scan_name: name = #scan_name; break;

	switch(scan)
	{
		MATCH_SCAN(escape)
		MATCH_SCAN(f1)
		MATCH_SCAN(f2)
		MATCH_SCAN(f3)
		MATCH_SCAN(f4)
		MATCH_SCAN(f5)
		MATCH_SCAN(f6)
		MATCH_SCAN(f7)
		MATCH_SCAN(f8)
		MATCH_SCAN(f9)
		MATCH_SCAN(f10)
		MATCH_SCAN(f11)
		MATCH_SCAN(f12)
		MATCH_SCAN(print)
		MATCH_SCAN(scroll_lock)
		MATCH_SCAN(pause)

		MATCH_SCAN(grave)
		MATCH_SCAN(1)
		MATCH_SCAN(2)
		MATCH_SCAN(3)
		MATCH_SCAN(4)
		MATCH_SCAN(5)
		MATCH_SCAN(6)
		MATCH_SCAN(7)
		MATCH_SCAN(8)
		MATCH_SCAN(9)
		MATCH_SCAN(0)
		MATCH_SCAN(minus)
		MATCH_SCAN(equal)
		MATCH_SCAN(backslash)
		MATCH_SCAN(backspace)

		MATCH_SCAN(tab)
		MATCH_SCAN(q)
		MATCH_SCAN(w)
		MATCH_SCAN(e)
		MATCH_SCAN(r)
		MATCH_SCAN(t)
		MATCH_SCAN(y)
		MATCH_SCAN(u)
		MATCH_SCAN(i)
		MATCH_SCAN(o)
		MATCH_SCAN(p)
		MATCH_SCAN(lbracket)
		MATCH_SCAN(rbracket)
		MATCH_SCAN(enter)

		MATCH_SCAN(caps_lock)
		MATCH_SCAN(a)
		MATCH_SCAN(s)
		MATCH_SCAN(d)
		MATCH_SCAN(f)
		MATCH_SCAN(g)
		MATCH_SCAN(h)
		MATCH_SCAN(j)
		MATCH_SCAN(k)
		MATCH_SCAN(l)
		MATCH_SCAN(semicolon)
		MATCH_SCAN(apostrophe)

		MATCH_SCAN(lshift)
		MATCH_SCAN(z)
		MATCH_SCAN(x)
		MATCH_SCAN(c)
		MATCH_SCAN(v)
		MATCH_SCAN(b)
		MATCH_SCAN(n)
		MATCH_SCAN(m)
		MATCH_SCAN(comma)
		MATCH_SCAN(period)
		MATCH_SCAN(slash)
		MATCH_SCAN(rshift)

		MATCH_SCAN(lcontrol)
		MATCH_SCAN(lsuper)
		MATCH_SCAN(lalt)
		MATCH_SCAN(space)
		MATCH_SCAN(ralt)
		MATCH_SCAN(rsuper)
		MATCH_SCAN(menu)
		MATCH_SCAN(rcontrol)

		MATCH_SCAN(insert)
		MATCH_SCAN(home)
		MATCH_SCAN(prior)
		MATCH_SCAN(delete)
		MATCH_SCAN(end)
		MATCH_SCAN(next)

		MATCH_SCAN(up)
		MATCH_SCAN(down)
		MATCH_SCAN(left)
		MATCH_SCAN(right)

		MATCH_SCAN(num_lock)
		MATCH_SCAN(kp_slash)
		MATCH_SCAN(kp_multiply)
		MATCH_SCAN(kp_subtract)
		MATCH_SCAN(kp_home)
		MATCH_SCAN(kp_up)
		MATCH_SCAN(kp_prior)
		MATCH_SCAN(kp_add)
		MATCH_SCAN(kp_left)
		MATCH_SCAN(kp_begin)
		MATCH_SCAN(kp_right)
		MATCH_SCAN(kp_end)
		MATCH_SCAN(kp_down)
		MATCH_SCAN(kp_next)
		MATCH_SCAN(kp_enter)
		MATCH_SCAN(kp_insert)
		MATCH_SCAN(kp_delete)

		MATCH_SCAN(lbutton)
		MATCH_SCAN(mbutton)
		MATCH_SCAN(rbutton)

		MATCH_SCAN(wheelup)
		MATCH_SCAN(wheeldown)

	    default: name = "unknown";
	}
	return name;

#undef	MATCH_SCAN
}

unsigned V_GLOBAL
uiName2Keyscan(char *name)
{
	unsigned code;
	if(!name || !*name)
		return sc_unknown;
	for(code = 0;code < sc_maxcode;code++)
		if(!strcasecmp(name,uiKeyscan2Name(code)))
			return code;
	return sc_unknown;
}

vsize_t V_CALL
archWinQueued(arch_window_p wnd)
{
	archWinPoll(wnd,false);
	return eventDeqCount(wnd->msg_que);
}

ui_event_p V_CALL
archWinPeek(arch_window_p wnd,vsize_t ofs)
{
	ui_event_p ptr;
	if(!eventDeqPeekBack(wnd->msg_que,ofs,&ptr))
		return NULL;
	return ptr;
}

void V_CALL
archWinAutoRepeat(arch_window_p aw,vboolean enable)
{
	aw->auto_repeat_disabled = !enable;
}

vboolean V_CALL
archWinRemove(arch_window_p wnd,vsize_t ofs)
{
	if(!eventDeqPeekBack(wnd->msg_que,ofs,NULL))
		return false;
	wnd->msg_que = eventDeqRemoveBack(wnd->msg_que,ofs);
	return true;
}

void V_CALL
archWinAutoWarp(arch_window_p aw,vboolean enable)
{
	if(enable)
	{
		int cx, cy;
		if(aw->auto_warp_enabled)
			return;
		aw->auto_warp_enabled = true;
		aw->auto_warp_saved_x = aw->last_mx;
		aw->auto_warp_saved_y = aw->last_my;
		cx = aw->width >> 1;
		cy = aw->height >> 1;
		aw->last_vmx = 0;
		aw->last_vmy = 0;
		aw->last_mx = cx;
		aw->last_my = cy;
		archWinShowPointer(aw, false);
		if(archWinWarpPointer(aw, cx, cy))
		{
			archWinGrabMouse(aw, true);
			return;
		}
		aw->auto_warp_enabled = false;
		aw->last_mx = aw->last_vmx = aw->auto_warp_saved_x;
		aw->last_my = aw->last_vmy = aw->auto_warp_saved_y;
		archWinShowPointer(aw, true);
	}
	if(!aw->auto_warp_enabled)
		return;
	aw->auto_warp_enabled = false;
	aw->last_mx = aw->last_vmx = aw->auto_warp_saved_x;
	aw->last_my = aw->last_vmy = aw->auto_warp_saved_y;
	archWinGrabMouse(aw, false);
	archWinWarpPointer(aw, aw->auto_warp_saved_x, aw->auto_warp_saved_y);
	archWinShowPointer(aw, true);
}

/*
 * ui_default_wnd_proc()
 * Returns 'false' if message must _not_ be stored in queue.
 */
vboolean V_CALL
ui_default_wnd_proc(arch_window_p aw,ui_event_p ev)
{
	int x, y, cx, cy, dx, dy;
	switch(ev->type)
	{
		case ui_pointer_motion_event:
			if(aw->zap_next_mousemove)
			{
				aw->zap_next_mousemove = false;
				return false;
			}
			x = ev->client_x;
			y = ev->client_y;
#ifdef	DISCARD_DUPLICATE_MOUSEMOVE
			if(aw->last_mx == x && aw->last_my == y)
				return false;
#endif	/* DISCARD_DUPLICATE_MOUSEMOVE */
			dx = x - aw->last_mx;
			dy = y - aw->last_my;
			if(aw->auto_warp_enabled)
            {
            	cx = aw->width >> 1;
            	cy = aw->height >> 1;
				aw->last_mx = cx;
				aw->last_my = cy;
				aw->last_vmx += dx;
				aw->last_vmy += dy;
				ev->client_x = aw->last_vmx;
				ev->client_y = aw->last_vmy;
				aw->zap_next_mousemove = true;
				archWinWarpPointer(aw, cx, cy);
				ev->client_dx = dx;
				ev->client_dy = dy;
            }
			else
			{
				aw->last_vmx = aw->last_mx = x;
				aw->last_vmy = aw->last_my = y;
			}
			return true;
		default:
			break;
	}
	if(aw->auto_warp_enabled)
    {
		ev->client_x = aw->last_vmx;
		ev->client_y = aw->last_vmy;
	}
	ev->client_dx = 0;
	ev->client_dy = 0;
	return true;
}

void V_CALL
ui_win_loose_control(arch_window_p aw,vboolean hiding)
{
	if(hiding)
	{
		/* check if we need to uninstall grabs / restore vidmode */
		if(aw->ms_grab_count > 0)
		{
			aw->saved_ms_grab_count = aw->ms_grab_count;
			aw->ms_grab_count = 1;
			archWinGrabMouse(aw, false);
		}
		if(aw->kb_grab_count > 0)
		{
			aw->saved_kb_grab_count = aw->kb_grab_count;
			aw->kb_grab_count = 1;
			archWinGrabKeyboard(aw, false);
		}
		if(aw->current_vidmode != -1)
		{
			aw->saved_vidmode = aw->current_vidmode;
			archWinSetVidMode(aw, -1);
		}
		/*
		 * On exit:
		 *  ms_grab_count == kb_grab_count == 0;
		 *  current_vidmode == 0;
		 */
	}
	else
	{
		/* check if we need to reinstall grabs / set vidmode */
		if(aw->saved_ms_grab_count > 0)
		{
			aw->ms_grab_count = 0;
			archWinGrabMouse(aw, true);
			aw->ms_grab_count = aw->saved_ms_grab_count;
			aw->saved_ms_grab_count = 0;
		}
		if(aw->saved_kb_grab_count > 0)
		{
			aw->kb_grab_count = 0;
			archWinGrabKeyboard(aw, true);
			aw->kb_grab_count = aw->saved_kb_grab_count;
			aw->saved_kb_grab_count = 0;
		}
		if(aw->saved_vidmode != -1)
		{
			archWinSetVidMode(aw, aw->saved_vidmode);
			aw->saved_vidmode = -1;
		}
		/*
		 * On exit:
		 *  saved_ms_grab_count == saved_kb_grab_count == 0;
		 *  saved_vidmode == -1;
		 */
	}
}

void V_CALL
ui_make_default_key_event(ui_key_event_p ev)
{
	ev->press = false;
	ev->scan = sc_unknown;
	ev->acode = 0;
	ev->ucode = 0;
}

void V_CALL
ui_set_configure_from_aw(arch_window_p aw,ui_configure_event_p ev)
{
	ev->focused = aw->focused;
	ev->visible = aw->visible;
	if(aw->visible)
	{
		ev->x = aw->x;
		ev->y = aw->y;
		ev->width = aw->width;
		ev->height = aw->height;
	}
	else
	{
		ev->x = ev->y = 0;
		ev->width = ev->height = 0;
	}
	ev->exposed_left = ev->exposed_top = 0;
	ev->exposed_right = ev->exposed_bottom = 0;
	ev->wm_event = ui_wm_unknown;
}

vec_t	V_CALL
ui_make_message_time(unsigned *start,unsigned current)
{
	unsigned delta1, delta2;
	if(!*start)
		*start = current;
	if(*start <= current)
	{
		delta1 = current - *start;
		delta2 = 0;
	}
	else
	{
		/* overflow ! */
		unsigned last;
#ifdef	_ARCH_64_
		last = 0xFFFFFFFFFFFFFFFF;
#else
		last = 0xFFFFFFFF;
#endif
		delta1 = last - *start;
		delta2 = current;
		/* FIXME: this is wrong */
		*start = current;
	}
	return (vec_t)0.001 * ((vec_t)delta1 + (vec_t)delta2);
}

/* BEGIN uicore.c */

vboolean V_GLOBAL
uiPending()
{
	CALLER_NAME(uiPending)
	gl_context_p ctx;
	IF_NO_GLCONTEXT(ctx)
		return false;
	archWinPoll(ctx->wnd,false);
	return ctx->wnd->msg_que ? true : false;
}

void V_GLOBAL
uiFlushQueue(void)
{
	CALLER_NAME(uiFlushQueue)
	gl_context_p ctx;
	IF_NO_GLCONTEXT(ctx)
		return;
	archWinPoll(ctx->wnd,false);
	if(eventDeqCount(ctx->wnd->msg_que))
	{
		eventDeqFree(ctx->wnd->msg_que);
		ctx->wnd->msg_que = NULL;
	}
}

ui_event_p V_GLOBAL
uiPeekEvent(vsize_t offset_from_first_push)
{
	CALLER_NAME(uiPeekEvent)
	ui_event_p ptr;
	gl_context_p ctx;
	IF_NO_GLCONTEXT(ctx)
		return NULL;
	if(!eventDeqPeekBack(ctx->wnd->msg_que,offset_from_first_push,&ptr))
		return NULL;
	return ptr;
}

ui_event_p V_GLOBAL
uiPeekLastEvent(vsize_t offset_from_last_push)
{
	CALLER_NAME(uiPeekLastEvent)
	ui_event_p ptr;
	gl_context_p ctx;
	IF_NO_GLCONTEXT(ctx)
		return NULL;
	if(!eventDeqPeek(ctx->wnd->msg_que,offset_from_last_push,&ptr))
		return NULL;
	return ptr;
}

vboolean V_GLOBAL
uiRemoveEvent(vsize_t offset_from_first_push,ui_event_p ev_return)
{
	CALLER_NAME(uiRemoveEvent)
	ui_event_p ptr;
	gl_context_p ctx;
	IF_NO_GLCONTEXT(ctx)
		return false;
	if(!eventDeqPeekBack(ctx->wnd->msg_que,offset_from_first_push,&ptr))
		return false;
	if(ev_return)
		memCopy(ev_return,ptr,sizeof(ui_event_t));
	ctx->wnd->msg_que = eventDeqRemoveBack(ctx->wnd->msg_que,
		offset_from_first_push);
	return true;
}

vboolean V_GLOBAL
uiRemoveLastEvent(vsize_t offset_from_last_push,ui_event_p ev_return)
{
	CALLER_NAME(uiRemoveLastEvent)
	ui_event_p ptr;
	gl_context_p ctx;
	IF_NO_GLCONTEXT(ctx)
		return false;
	if(!eventDeqPeek(ctx->wnd->msg_que,offset_from_last_push,&ptr))
		return false;
	if(ev_return)
		memCopy(ev_return,ptr,sizeof(ui_event_t));
	ctx->wnd->msg_que =
		eventDeqRemove(ctx->wnd->msg_que,offset_from_last_push);
	return true;
}

vboolean V_GLOBAL
uiNextEvent(ui_event_p ev_return)
{
	CALLER_NAME(uiNextEvent)
	gl_context_p ctx;
	IF_NO_GLCONTEXT(ctx)
		return false;
	archWinPoll(ctx->wnd,true);
	ctx->wnd->msg_que = eventDeqPopBack(ctx->wnd->msg_que,ev_return);
	return true;
}
