/*
   Copyright (C) 1997-2001 Id Software, Inc.

   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 <SDL.h>
#include "../client/client.h"

cvar_t *in_grabinconsole;

static qboolean input_inited = qfalse;
static qboolean mouse_active = qfalse;
static qboolean input_active = qfalse;

static int mx, my;

void IN_Commands( void )
{
}
void IN_Activate( qboolean active )
{
}


/**
 * Function which is called whenever the mouse is moved.
 * @param ev the SDL event object containing the mouse position et all
 */
static void _mouse_motion_event( SDL_MouseMotionEvent *event )
{
	mx += event->xrel;
	my += event->yrel;
}


/**
 * Function which is called whenever a mouse button is pressed or released.
 * @param ev the SDL event object containing the button number et all
 * @param state either qtrue if it is a keydown event or qfalse otherwise
 */
static void _mouse_button_event( SDL_MouseButtonEvent *event, qboolean state )
{
	Uint8 button = event->button;
	if( button <= 5 )
	{
		// By convention button 2 is the right button and 3 is the middle
		// button. SDL reports the middle mouse button as button 2 so we
		// need to swap the meaning of button 2 and button 3 here.
		switch( button )
		{
		case 1: Key_Event( K_MOUSE1, state, Sys_Milliseconds() ); break;
		case 2: Key_Event( K_MOUSE3, state, Sys_Milliseconds() ); break;
		case 3: Key_Event( K_MOUSE2, state, Sys_Milliseconds() ); break;
		case 4: Key_Event( K_MWHEELUP, state, Sys_Milliseconds() ); break;
		case 5: Key_Event( K_MWHEELDOWN, state, Sys_Milliseconds() ); break;
		}
	}
	else if( button <= 10 )
	{
		// The engine only supports up to 8 buttons plus the mousewheel.
		Key_Event( K_MOUSE1 + button - 3, state, Sys_Milliseconds() );
	}
	else
		Com_Printf( "sdl_input.c: Unsupported mouse button (button = %u)\n", button );
}


/**
 * Function which is called whenever a key is pressed or released.
 * @param event the SDL event object containing the keysym et all
 * @param state either qtrue if it is a keydown event or qfalse otherwise
 */
static void _key_event( const SDL_KeyboardEvent *event, const qboolean state )
{
	SDLKey sdlKey = event->keysym.sym;

	// qKey is guaranteed to be initialized before calling Key_Event
	keyNum_t qKey;

	if( sdlKey >= 32 && sdlKey < 128 )
	{
		// The engine doesn't use any special codes for visible ASCII
		// characters. So the key can be passed to the engine without
		// looking it up first.
		if( state )
			qKey = event->keysym.unicode;
		else
			qKey = sdlKey;
	}
	else
	{
		switch( sdlKey )
		{
			//key above tab on international keyboard layouts, make it activate console
		case SDLK_WORLD_0:		qKey = 96;		break;
		case SDLK_BACKSPACE:    qKey = K_BACKSPACE;	break;
		case SDLK_TAB:          qKey = K_TAB;		break;
		case SDLK_RETURN:       qKey = K_ENTER;		break;
		case SDLK_PAUSE:        qKey = K_PAUSE;		break;
		case SDLK_ESCAPE:       qKey = K_ESCAPE;	break;
		case SDLK_DELETE:		qKey = K_DEL;		break;

			/* Numeric keypad */
		case SDLK_KP0:          qKey = KP_INS;           break;
		case SDLK_KP1:          qKey = KP_END;           break;
		case SDLK_KP2:          qKey = KP_DOWNARROW;     break;
		case SDLK_KP3:          qKey = KP_PGDN;          break;
		case SDLK_KP4:          qKey = KP_LEFTARROW;     break;
		case SDLK_KP5:          qKey = KP_5;             break;
		case SDLK_KP6:          qKey = KP_RIGHTARROW;    break;
		case SDLK_KP7:          qKey = KP_HOME;          break;
		case SDLK_KP8:          qKey = KP_UPARROW;       break;
		case SDLK_KP9:          qKey = KP_PGUP;          break;
		case SDLK_KP_PERIOD:    qKey = KP_DEL;           break;
		case SDLK_KP_DIVIDE:    qKey = KP_SLASH;         break;
		case SDLK_KP_MULTIPLY:  qKey = KP_STAR;          break;
		case SDLK_KP_MINUS:     qKey = KP_MINUS;         break;
		case SDLK_KP_PLUS:      qKey = KP_PLUS;          break;
		case SDLK_KP_ENTER:     qKey = KP_ENTER;         break;

			/* Arrows + Home/End pad */
		case SDLK_UP:           qKey = K_UPARROW;        break;
		case SDLK_DOWN:         qKey = K_DOWNARROW;      break;
		case SDLK_RIGHT:        qKey = K_RIGHTARROW;     break;
		case SDLK_LEFT:         qKey = K_LEFTARROW;      break;
		case SDLK_INSERT:       qKey = K_INS;            break;
		case SDLK_HOME:         qKey = K_HOME;           break;
		case SDLK_END:          qKey = K_END;            break;
		case SDLK_PAGEUP:       qKey = K_PGUP;           break;
		case SDLK_PAGEDOWN:     qKey = K_PGDN;           break;

			/* Function keys */
		case SDLK_F1:           qKey = K_F1;             break;
		case SDLK_F2:           qKey = K_F2;             break;
		case SDLK_F3:           qKey = K_F3;             break;
		case SDLK_F4:           qKey = K_F4;             break;
		case SDLK_F5:           qKey = K_F5;             break;
		case SDLK_F6:           qKey = K_F6;             break;
		case SDLK_F7:           qKey = K_F7;             break;
		case SDLK_F8:           qKey = K_F8;             break;
		case SDLK_F9:           qKey = K_F9;             break;
		case SDLK_F10:          qKey = K_F10;            break;
		case SDLK_F11:          qKey = K_F11;            break;
		case SDLK_F12:          qKey = K_F12;            break;
		case SDLK_F13:          qKey = K_F13;            break;
		case SDLK_F14:          qKey = K_F14;            break;
		case SDLK_F15:          qKey = K_F15;            break;

			/* Key state modifier keys */
		case SDLK_NUMLOCK:      qKey = K_NUMLOCK;	break;
		case SDLK_CAPSLOCK:     qKey = K_CAPSLOCK;	break;
		case SDLK_RSHIFT:       qKey = K_SHIFT;		break;
		case SDLK_LSHIFT:       qKey = K_SHIFT;		break;
		case SDLK_RCTRL:        qKey = K_CTRL;		break;
		case SDLK_LCTRL:        qKey = K_CTRL;		break;
		case SDLK_RALT:         qKey = K_ALT;		break;
		case SDLK_LALT:         qKey = K_ALT;		break;
		case SDLK_RMETA:        qKey = K_COMMAND;	break;
		case SDLK_LMETA:        qKey = K_COMMAND;	break;

		default:
			if( state == qtrue )
			{
				Com_Printf( "sdl_input.c: Unsupported key (SDL Keysym = %u, unicode = %u)\n", sdlKey, event->keysym.unicode );
				//return;
				qKey = event->keysym.unicode;
			}
		}

	}

	// And finally let the engine know about the key event
	Key_Event( qKey, state, Sys_Milliseconds() );
	if( state == qtrue && ( qKey >= 32 && qKey <= 126 ) )
	{
		// The console and other text fields are only interrested in
		// key down events. Those events are called char event.
		Key_CharEvent( qKey, qKey );
	}
}

/*****************************************************************************/

static void HandleEvents( void )
{
	SDL_Event event;

	while( SDL_PollEvent( &event ) )
	{
		switch( event.type )
		{
		case SDL_KEYDOWN:
			_key_event( &event.key, qtrue );
			break;

		case SDL_KEYUP:
			_key_event( &event.key, qfalse );
			break;

		case SDL_MOUSEMOTION:
			_mouse_motion_event( &event.motion );
			break;

		case SDL_MOUSEBUTTONDOWN:
			_mouse_button_event( &event.button, qtrue );
			break;

		case SDL_MOUSEBUTTONUP:
			_mouse_button_event( &event.button, qfalse );
			break;

		case SDL_QUIT:
			Sys_Quit();
			break;

		}

	}

}

void IN_Move( usercmd_t *cmd )
{
	if( ( mx || my ) && mouse_active )
	{
		CL_MouseMove( cmd, mx, my );
		mx = my = 0;
	}
}

void IN_Init()
{
	if( input_inited )
		return;

	SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
	SDL_ShowCursor( SDL_DISABLE );
	SDL_EnableUNICODE( SDL_ENABLE );
	SDL_WM_GrabInput( SDL_GRAB_ON );
	SDL_SetCursor( NULL );

	in_grabinconsole = Cvar_Get( "in_grabinconsole", "0", CVAR_ARCHIVE );

	input_inited = qtrue;
	input_active = qtrue; // will be activated by IN_Frame if necessary
}

/**
 * Shutdown input subsystem.
 */
void IN_Shutdown()
{
	if( !input_inited )
		return;

	IN_Activate( qfalse );
	input_inited = qfalse;

	SDL_EnableUNICODE( SDL_DISABLE );
	SDL_WM_GrabInput( SDL_GRAB_OFF );
}

/**
 * Restart the input subsystem.
 */
void IN_Restart( void )
{
	IN_Shutdown();
	IN_Init();
}

/**
 * This function is called for every frame and gives us some time to poll
 * for events that occured at our input devices.
 */
void IN_Frame()
{
	if( !input_inited )
		return;

	if( !Cvar_Value( "vid_fullscreen" ) && cls.key_dest == key_console && !in_grabinconsole->integer )
	{
		mouse_active = qfalse;
		input_active = qtrue;
		SDL_WM_GrabInput( SDL_GRAB_OFF );
	}
	else
	{
		mouse_active = qtrue;
		input_active = qtrue;
		SDL_WM_GrabInput( SDL_GRAB_ON );
	}

	HandleEvents();
}
