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

This file is part of Quake 2 source code.

Quake 2 source code 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.

Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// menu_options_keys.c -- the key binding menu

#include <ctype.h>
#ifdef _WIN32
#include <io.h>
#endif
#include "../client/client.h"
#include "ui_local.h"

#define USE_KEYBIND_CONTROL

/*
=======================================================================

KEYS MENU

=======================================================================
*/

char *bindnames[][2] =
{
{"+attack", 		"attack"},
{"+attack2", 		"alternate attack"},
{"+use", 			"activate"},
{"weapprev", 		"prev weapon"},
{"weapnext", 		"next weapon"},
{"+forward", 		"walk forward"},
{"+back", 			"backpedal"},
{"+left", 			"turn left"},
{"+right", 			"turn right"},
{"+speed", 			"run"},
{"+moveleft", 		"step left"},
{"+moveright", 		"step right"},
{"+strafe", 		"sidestep"},
{"+lookup", 		"look up"},
{"+lookdown", 		"look down"},
{"centerview", 		"center view"},
{"+mlook", 			"mouse look"},
{"+klook", 			"keyboard look"},
{"+moveup",			"up / jump"},
{"+movedown",		"down / crouch"},
{"inven",			"inventory"},
{"invuse",			"use item"},
{"invdrop",			"drop item"},
{"invprev",			"prev item"},
{"invnext",			"next item"},
{"cmd help", 		"help computer" }, 
{ 0, 0 }
};

#ifndef USE_KEYBIND_CONTROL
int				keys_cursor;
static int		bind_grab;
#endif

static menuframework_s	s_keys_menu;
#ifdef USE_KEYBIND_CONTROL
static menukeybind_s	s_keys_binds[64];
#else
static menuaction_s		s_keys_binds[64];
#endif
static menuaction_s		s_keys_back_action;

//=======================================================================

#ifndef USE_KEYBIND_CONTROL
static void M_UnbindCommand (char *command)
{
	int		j;
	int		l;
	char	*b;

	l = (int)strlen(command);

	for (j=0 ; j<256 ; j++)
	{
		b = keybindings[j];
		if (!b)
			continue;
		// Knightmare- fix bug with key bound to +attack2 being confused with +attack
		if (!strncmp (b, "+attack2", 8) && strncmp (command, "+attack2", 8))
			continue;
		if (!strncmp (b, command, l) )
			Key_SetBinding (j, "");
	}
}

static void M_FindKeysForCommand (char *command, int *twokeys)
{
	int		count, j, l;
	char	*b;

	twokeys[0] = twokeys[1] = -1;
	l = (int)strlen(command);
	count = 0;

	for (j=0 ; j<256 ; j++)
	{
		b = keybindings[j];
		if (!b)
			continue;
		// Knightmare- fix bug with key bound to +attack2 being confused with +attack
		if (!strncmp (b, "+attack2", 8) && strncmp (command, "+attack2", 8))
			continue;
		if (!strncmp (b, command, l))
		{
			twokeys[count] = j;
			count++;
			if (count == 2)
				break;
		}
	}
}

static void M_KeysBackCursorDrawFunc (menuaction_s *self) // back action
{
	char	*cursor;

	cursor = ((int)(Sys_Milliseconds()/250)&1) ? UI_ITEMCURSOR_DEFAULT_PIC : UI_ITEMCURSOR_BLINK_PIC;

	UI_DrawPic (SCREEN_WIDTH*0.5 - 24, s_keys_menu.y + self->generic.y, MENU_FONT_SIZE, MENU_FONT_SIZE, ALIGN_CENTER, false, cursor, 255);
/*	UI_DrawChar (SCREEN_WIDTH*0.5 - 24, s_keys_menu.y + self->generic.y, MENU_FONT_SIZE, ALIGN_CENTER,
					12+((int)(Sys_Milliseconds()/250)&1), 255, 255, 255, 255, false, true);
*/
}

static void M_KeyCursorDrawFunc (menuframework_s *menu)
{
	char	*cursor;

	if (bind_grab)
		cursor = UI_ITEMCURSOR_KEYBIND_PIC;
	else
		cursor = ((int)(Sys_Milliseconds()/250)&1) ? UI_ITEMCURSOR_DEFAULT_PIC : UI_ITEMCURSOR_BLINK_PIC;

	UI_DrawPic (menu->x, menu->y + menu->cursor * MENU_LINE_SIZE, MENU_FONT_SIZE, MENU_FONT_SIZE, ALIGN_CENTER, false, cursor, 255);
/*	if (bind_grab)
		UI_DrawChar (menu->x, menu->y + menu->cursor * MENU_LINE_SIZE, MENU_FONT_SIZE, ALIGN_CENTER,
						'=', 255, 255, 255, 255, false, true);
	else
		UI_DrawChar (menu->x, menu->y + menu->cursor * MENU_LINE_SIZE, MENU_FONT_SIZE, ALIGN_CENTER,
						12+((int)(Sys_Milliseconds()/250)&1), 255, 255, 255, 255, false, true);
*/
}

static void M_DrawKeyBindingFunc (void *self)
{
	int keys[2];
	menuaction_s *a = (menuaction_s *) self;

	M_FindKeysForCommand( bindnames[a->generic.localdata[0]][0], keys);
		
	if (keys[0] == -1)
	{
		UI_DrawString (a->generic.x + a->generic.parent->x + 16,
						a->generic.y + a->generic.parent->y, a->generic.textSize, "???", 255);
	}
	else
	{
		int x;
		const char *name;

		name = Key_KeynumToString (keys[0]);

		UI_DrawString (a->generic.x + a->generic.parent->x + 16,
						a->generic.y + a->generic.parent->y, a->generic.textSize, name , 255);

		x = (int)strlen(name) * MENU_FONT_SIZE;

		if (keys[1] != -1)
		{
			UI_DrawString (a->generic.x + a->generic.parent->x + MENU_FONT_SIZE*3 + x,
							a->generic.y + a->generic.parent->y, a->generic.textSize, "or", 255);
			UI_DrawString (a->generic.x + a->generic.parent->x + MENU_FONT_SIZE*6 + x,
							a->generic.y + a->generic.parent->y, a->generic.textSize, Key_KeynumToString(keys[1]), 255);
		}
	}
}

static void M_KeyBindingFunc (void *self)
{
	menuaction_s *a = ( menuaction_s * ) self;
	int keys[2];

	M_FindKeysForCommand (bindnames[a->generic.localdata[0]][0], keys);

	if (keys[1] != -1)
		M_UnbindCommand (bindnames[a->generic.localdata[0]][0]);

	bind_grab = true;

	UI_SetMenuStatusBar (&s_keys_menu, "press a key or button for this action");
}

void M_AddBindOption (int i, char *list[][2])
{
	s_keys_binds[i].generic.type	= MTYPE_ACTION;
	s_keys_binds[i].generic.textSize = MENU_FONT_SIZE;
	s_keys_binds[i].generic.flags  = QMF_GRAYED;
	s_keys_binds[i].generic.x		= 0;
	s_keys_binds[i].generic.y		= i*MENU_LINE_SIZE;
	s_keys_binds[i].generic.ownerdraw = M_DrawKeyBindingFunc;
	s_keys_binds[i].generic.localdata[0] = i;
	s_keys_binds[i].generic.name	= list[s_keys_binds[i].generic.localdata[0]][1];
	s_keys_binds[i].generic.callback = M_KeyBindingFunc;

	if (strstr ("MENUSPACE", list[i][0]))
		s_keys_binds[i].generic.type	= MTYPE_SEPARATOR;
}
#endif	// USE_KEYBIND_CONTROL

//=======================================================================

static void Menu_Keys_Init (void)
{
	int		BINDS_MAX;
	int		i = 0, x = 0, y = 0;

	s_keys_menu.x = SCREEN_WIDTH*0.5;
	s_keys_menu.y = SCREEN_HEIGHT*0.5 - 72;
	s_keys_menu.nitems = 0;
#ifndef USE_KEYBIND_CONTROL
	s_keys_menu.cursordraw = M_KeyCursorDrawFunc;
#endif

	BINDS_MAX = listSize(bindnames);
	for (i=0;i<BINDS_MAX;i++)
#ifdef USE_KEYBIND_CONTROL
	for (i=0; i<BINDS_MAX; i++)
	{
		s_keys_binds[i].generic.type			= MTYPE_KEYBIND;
		s_keys_binds[i].generic.flags			= QMF_ALTCOLOR;
		s_keys_binds[i].generic.x				= x;
		s_keys_binds[i].generic.y				= y + i*MENU_LINE_SIZE;
		s_keys_binds[i].generic.name			= bindnames[i][1];
		s_keys_binds[i].generic.statusbar		= "enter or mouse1 to change, backspace or del to clear";
		s_keys_binds[i].commandName				= bindnames[i][0];
		s_keys_binds[i].enter_statusbar			= "press a key or button for this action";
	}
#else
		M_AddBindOption (i, bindnames);
#endif

	s_keys_back_action.generic.type		= MTYPE_ACTION;
	s_keys_back_action.generic.textSize	= MENU_FONT_SIZE;
	s_keys_back_action.generic.flags	= QMF_LEFT_JUSTIFY;
	s_keys_back_action.generic.x		= x;
	s_keys_back_action.generic.y		= y + (BINDS_MAX+2)*MENU_LINE_SIZE;
	s_keys_back_action.generic.name		= "Back";
	s_keys_back_action.generic.callback	= UI_BackMenu;
#ifndef USE_KEYBIND_CONTROL
	s_keys_back_action.generic.cursordraw = M_KeysBackCursorDrawFunc;
#endif

	for (i=0;i<BINDS_MAX;i++)
		UI_AddMenuItem (&s_keys_menu, (void *) &s_keys_binds[i]);
	UI_AddMenuItem (&s_keys_menu, (void *) &s_keys_back_action);

#ifndef USE_KEYBIND_CONTROL
	UI_SetMenuStatusBar (&s_keys_menu, "enter or mouse1 to change, backspace to clear");
#endif
	// Don't center it- it's too large
//	UI_CenterMenu (&s_keys_menu);
}

static void Menu_Keys_Draw (void)
{
	UI_DrawBanner ("m_banner_customize"); // Knightmare added

	UI_AdjustMenuCursor (&s_keys_menu, 1);
	UI_DrawMenu (&s_keys_menu);
}

static const char *Menu_Keys_Key (int key)
{
#ifdef USE_KEYBIND_CONTROL
	return UI_DefaultMenuKey (&s_keys_menu, key);
#else
	menuaction_s *item = (menuaction_s *) UI_ItemAtMenuCursor( &s_keys_menu );

	// pressing mouse1 to pick a new bind wont force bind/unbind itself - spaz
	if ( bind_grab && !(ui_mousecursor.buttonused[MOUSEBUTTON1]&&key==K_MOUSE1))
	{	
		if ( key != K_ESCAPE && key != '`' )
		{
			char cmd[1024];

			Com_sprintf (cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), bindnames[item->generic.localdata[0]][0]);
			Cbuf_InsertText (cmd);
		}
		
		//  Knightmare- added Psychospaz's mouse support
		//dont let selecting with mouse buttons screw everything up
		UI_RefreshCursorButtons();
		if (key == K_MOUSE1)
			ui_mousecursor.buttonclicks[MOUSEBUTTON1] = -1;

		UI_SetMenuStatusBar (&s_keys_menu, "enter to change, backspace to clear");
		bind_grab = false;
		return ui_menu_out_sound;
	}

	switch (key)
	{
	case K_KP_ENTER:
	case K_ENTER:
		if (item == &s_keys_back_action) { // back action hack
			UI_BackMenu(item); return NULL; }
		M_KeyBindingFunc (item);
		return ui_menu_in_sound;
	case K_BACKSPACE:		// delete bindings
	case K_DEL:				// delete bindings
	case K_KP_DEL:
		M_UnbindCommand (bindnames[item->generic.localdata[0]][0]);
		return ui_menu_out_sound;
	default:
		return UI_DefaultMenuKey (&s_keys_menu, key);
	}
#endif
}

void Menu_Keys_f (void)
{
	Menu_Keys_Init ();
	UI_PushMenu (Menu_Keys_Draw, Menu_Keys_Key);
}