/*
Copyright (C) 2006-2007 Mark Olsen

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <exec/exec.h>
#include <intuition/intuition.h>
#include <intuition/extensions.h>
#include <intuition/intuitionbase.h>
#include <devices/input.h>

#include <proto/exec.h>
#include <proto/intuition.h>

#include <clib/alib_protos.h>

#include "quakedef.h"
#include "input.h"

#include "in_morphos.h"

struct InputEvent imsgs[MAXIMSGS];
extern struct IntuitionBase *IntuitionBase;
extern struct Window *window;
extern struct Screen *screen;

int imsglow = 0;
int imsghigh = 0;

extern qboolean mouse_active;

static struct Interrupt InputHandler;

static struct Interrupt InputHandler;
static struct MsgPort *inputport = 0;
static struct IOStdReq *inputreq = 0;
static BYTE inputret = -1;

cvar_t m_filter = {"m_filter", "1", CVAR_ARCHIVE};

extern cvar_t _windowed_mouse;

float mouse_x, mouse_y;
float old_mouse_x, old_mouse_y;

#define DEBUGRING(x)

void IN_Shutdown(void)
{
	if (inputret == 0)
	{
		inputreq->io_Data = (void *)&InputHandler;
		inputreq->io_Command = IND_REMHANDLER;
		DoIO((struct IORequest *)inputreq);

		CloseDevice((struct IORequest *)inputreq);

		inputret = -1;
	}

	if (inputreq)
	{
		DeleteStdIO(inputreq);

		inputreq = 0;
	}

	if (inputport)
	{
		DeletePort(inputport);

		inputport = 0;
	}
}

void IN_ReInit()
{
/*	Cvar_Register (&m_filter, "input controls");*/

	inputport = CreatePort(0, 0);
	if (inputport == 0)
	{
		IN_Shutdown();
		Sys_Error("Unable to create message port");
	}

	inputreq = CreateStdIO(inputport);
	if (inputreq == 0)
	{
		IN_Shutdown();
		Sys_Error("Unable to create IO request");
	}

	inputret = OpenDevice("input.device", 0, (struct IORequest *)inputreq, 0);
	if (inputret != 0)
	{
		IN_Shutdown();
		Sys_Error("Unable to open input.device");
	}

	InputHandler.is_Node.ln_Type = NT_INTERRUPT;
	InputHandler.is_Node.ln_Pri = 100;
	InputHandler.is_Node.ln_Name = "FTEQW input handler";
	InputHandler.is_Data = 0;
	InputHandler.is_Code = (void(*)())&myinputhandler;
	inputreq->io_Data = (void *)&InputHandler;
	inputreq->io_Command = IND_ADDHANDLER;
	DoIO((struct IORequest *)inputreq);
}

void IN_Init(void)
{
	IN_ReInit();
}

static void ExpireRingBuffer()
{
	int i = 0;

	while(imsgs[imsglow].ie_Class == IECLASS_NULL && imsglow != imsghigh)
	{
		imsglow++;
		i++;
		imsglow%= MAXIMSGS;
	}

	DEBUGRING(dprintf("Expired %d messages\n", i));
}

void IN_Commands(void)
{
	int a;
	char key;
	int i;
	int down;
	struct InputEvent ie;

	for(i = imsglow;i != imsghigh;i++, i%= MAXIMSGS)
	{
		DEBUGRING(dprintf("%d %d\n", i, imsghigh));
		if ((window->Flags & WFLG_WINDOWACTIVE))
		{
			if (imsgs[i].ie_Class == IECLASS_NEWMOUSE)
			{
				key = 0;

				if (imsgs[i].ie_Code == NM_WHEEL_UP)
					key = K_MWHEELUP;
				else if (imsgs[i].ie_Code == NM_WHEEL_DOWN)
					key = K_MWHEELDOWN;

				if (imsgs[i].ie_Code == NM_BUTTON_FOURTH)
				{
					Key_Event(0, K_MOUSE4, 0, true);
				}
				else if (imsgs[i].ie_Code == (NM_BUTTON_FOURTH|IECODE_UP_PREFIX))
				{
					Key_Event(0, K_MOUSE4, 0, false);
				}

				if (key)
				{
					Key_Event(0, key, 0, 1);
					Key_Event(0, key, 0, 0);
				}

			}
			else if (imsgs[i].ie_Class == IECLASS_RAWKEY)
			{
				down = !(imsgs[i].ie_Code&IECODE_UP_PREFIX);
				imsgs[i].ie_Code&=~IECODE_UP_PREFIX;

				memcpy(&ie, &imsgs[i], sizeof(ie));

				key = 0;
				if (imsgs[i].ie_Code <= 255)
					key = keyconv[imsgs[i].ie_Code];

				if (key)
					Key_Event(0, key, key, down);
				else
				{
					if (developer.value)
						Con_Printf("Unknown key %d\n", imsgs[i].ie_Code);
				}
			}

			else if (imsgs[i].ie_Class == IECLASS_RAWMOUSE)
			{
				if (imsgs[i].ie_Code == IECODE_LBUTTON)
					Key_Event(0, K_MOUSE1, 0, true);
				else if (imsgs[i].ie_Code == (IECODE_LBUTTON|IECODE_UP_PREFIX))
					Key_Event(0, K_MOUSE1, 0, false);
				else if (imsgs[i].ie_Code == IECODE_RBUTTON)
					Key_Event(0, K_MOUSE2, 0, true);
				else if (imsgs[i].ie_Code == (IECODE_RBUTTON|IECODE_UP_PREFIX))
					Key_Event(0, K_MOUSE2, 0, false);
				else if (imsgs[i].ie_Code == IECODE_MBUTTON)
					Key_Event(0, K_MOUSE3, 0, true);
				else if (imsgs[i].ie_Code == (IECODE_MBUTTON|IECODE_UP_PREFIX))
					Key_Event(0, K_MOUSE3, 0, false);

				mouse_x+= imsgs[i].ie_position.ie_xy.ie_x;
				mouse_y+= imsgs[i].ie_position.ie_xy.ie_y;
			}

		}

		imsgs[i].ie_Class = IECLASS_NULL;

	}

	ExpireRingBuffer();

}

void IN_Move (float *movements, int pnum)
{
	extern int mousecursor_x, mousecursor_y;
	extern int mousemove_x, mousemove_y;

	if (pnum != 0)
		return;	//we're lazy today.

	if (m_filter.value) {
		mouse_x = (mouse_x + old_mouse_x) * 0.5;
		mouse_y = (mouse_y + old_mouse_y) * 0.5;
	}

	old_mouse_x = mouse_x;
	old_mouse_y = mouse_y;

	if(in_xflip.value) mouse_x *= -1;

   	if (Key_MouseShouldBeFree())
	{
		mousemove_x += mouse_x;
		mousemove_y += mouse_y;
		mousecursor_x += mouse_x;
		mousecursor_y += mouse_y;

		if (mousecursor_y<0)
			mousecursor_y=0;
		if (mousecursor_x<0)
			mousecursor_x=0;

		if (mousecursor_x >= vid.width)
			mousecursor_x = vid.width - 1;

		if (mousecursor_y >= vid.height)
			mousecursor_y = vid.height - 1;

		mouse_x = mouse_y = 0;
#ifdef VM_UI
		UI_MousePosition(mousecursor_x, mousecursor_y);
#endif
	}


	mouse_x *= sensitivity.value;
	mouse_y *= sensitivity.value;
   
	if ( (in_strafe.state[pnum] & 1) || (lookstrafe.value && (in_mlook.state[pnum] & 1) ))
	{
		if (movements)
			movements[1] += m_side.value * mouse_x;
	}
	else
	{
		cl.playerview[pnum].viewanglechange[YAW] -= m_yaw.value * mouse_x;
	}
	if (in_mlook.state[pnum] & 1)
		V_StopPitchDrift (pnum);
   
	if ( (in_mlook.state[pnum] & 1) && !(in_strafe.state[pnum] & 1)) {
		cl.playerview[pnum].viewanglechange[PITCH] += m_pitch.value * mouse_y;
	} else {
		if (movements)
		{
			if ((in_strafe.state[pnum] & 1) && noclip_anglehack)
				movements[2] -= m_forward.value * mouse_y;
			else
				movements[0] -= m_forward.value * mouse_y;
		}
	}
	mouse_x = mouse_y = 0.0;
}

char keyconv[] =
{
	'`', /* 0 */
	'1',
	'2',
	'3',
	'4',
	'5',
	'6',
	'7',
	'8',
	'9',
	'0', /* 10 */
	'-',
	'=',
	0,
	0,
	K_INS,
	'q',
	'w',
	'e',
	'r',
	't', /* 20 */
	'y',
	'u',
	'i',
	'o',
	'p',
	'[',
	']',
	0,
	K_KP_END,
	K_KP_DOWNARROW, /* 30 */
	K_KP_PGDN,
	'a',
	's',
	'd',
	'f',
	'g',
	'h',
	'j',
	'k',
	'l', /* 40 */
	';',
	'\'',
	'\\',
	0,
	K_KP_LEFTARROW,
	K_KP_5,
	K_KP_RIGHTARROW,
	'<',
	'z',
	'x', /* 50 */
	'c',
	'v',
	'b',
	'n',
	'm',
	',',
	'.',
	'/',
	0,
	K_KP_DEL, /* 60 */
	K_KP_HOME,
	K_KP_UPARROW,
	K_KP_PGUP,
	' ',
	K_BACKSPACE,
	K_TAB,
	K_KP_ENTER,
	K_ENTER,
	K_ESCAPE,
	K_DEL, /* 70 */
	K_INS,
	K_PGUP,
	K_PGDN,
	K_KP_MINUS,
	K_F11,
	K_UPARROW,
	K_DOWNARROW,
	K_RIGHTARROW,
	K_LEFTARROW,
	K_F1, /* 80 */
	K_F2,
	K_F3,
	K_F4,
	K_F5,
	K_F6,
	K_F7,
	K_F8,
	K_F9,
	K_F10,
	0, /* 90 */
	0,
	K_KP_SLASH,
	0,
	K_KP_PLUS,
	0,
	K_SHIFT,
	K_SHIFT,
	K_CAPSLOCK,
	K_CTRL,
	K_ALT, /* 100 */
	K_ALT,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	K_PAUSE, /* 110 */
	K_F12,
	K_HOME,
	K_END,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 120 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 130 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 140 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 150 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 160 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 170 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 180 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 190 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 200 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 210 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 220 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 230 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 240 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0, /* 250 */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0
};

struct InputEvent *myinputhandler_real(void);

struct EmulLibEntry myinputhandler =
{
	TRAP_LIB, 0, (void(*)(void))myinputhandler_real
};

struct InputEvent *myinputhandler_real()
{
	struct InputEvent *moo = (struct InputEvent *)REG_A0;

	struct InputEvent *coin;

	int screeninfront;

	coin = moo;

	if (screen)
	{
#if 0
		if (IntuitionBase->LibNode.lib_Version > 50 || (IntuitionBase == 50 && IntuitionBase->LibNode.lib_Revision >= 56))
			GetAttr(screen, SA_Displayed, &screeninfront);
		else
#endif
			screeninfront = screen==IntuitionBase->FirstScreen;
	}
	else
		screeninfront = 1;

	do
	{
		if (coin->ie_Class == IECLASS_RAWMOUSE || coin->ie_Class == IECLASS_RAWKEY || coin->ie_Class == IECLASS_NEWMOUSE)
		{
/*			kprintf("Mouse\n");*/

			if ((imsghigh > imsglow && !(imsghigh == MAXIMSGS-1 && imsglow == 0)) || (imsghigh < imsglow && imsghigh != imsglow-1) || imsglow == imsghigh)
			{
				memcpy(&imsgs[imsghigh], coin, sizeof(imsgs[0]));
				imsghigh++;
				imsghigh%= MAXIMSGS;
			}
			else
			{
				DEBUGRING(kprintf("FTE: message dropped, imsglow = %d, imsghigh = %d\n", imsglow, imsghigh));
			}

			if (/*mouse_active && */(window->Flags & WFLG_WINDOWACTIVE) && coin->ie_Class == IECLASS_RAWMOUSE && screeninfront && window->MouseX > 0 && window->MouseY > 0)
			{
				if (_windowed_mouse.value)
				{
#if 0
					coin->ie_Class = IECLASS_NULL;
#else
					coin->ie_position.ie_xy.ie_x = 0;
					coin->ie_position.ie_xy.ie_y = 0;
#endif
				}
			}
		}

		coin = coin->ie_NextEvent;
	} while(coin);

	return moo;
}