/* newmenu-controls.m,
 *
 * Dodgy, dodgy code!
 */

#include <allegro.h>
#include <objc/Object.h>
#include "common.h"
#include "input.h"
#include "key-names.h"
#include "newmenu-controls.h"
#include "newmenu-dirty.h"
#include "newmenu-log.h"
#include "newmenu.h"
#include "seborrhea/seborrhea-allegro.h"
#include "seborrhea/seborrhea-font.h"
#include "seborrhea/seborrhea.h"


#define PICK(n,t,f)		((selected == n) ? t : f)
#define PICK_RGB(n)		PICK(n,rs,rn):PICK(n,gs,gn):PICK(n,bs,bn)


static SebFont *menu_font;

/*--------------------------------------------------------------*/
/* Menu navigation stuff.					*/
/*--------------------------------------------------------------*/

#define EXIT_ITEM		6
#define JOY_NUM_ITEM		7
#define EXIT_ITEM2		11

static struct {
    int up, down, left, right;
} menu_navigation_things[] = {
    {  0,  3,  1,  2 },			/*  0: KB up	*/
    {  0,  3,  1,  2 },			/*  1: KB left	*/
    {  0,  3,  1,  7 },			/*  2: KB right	*/
    {  0,  4,  1,  2 },			/*  3: KB down	*/
    {  3,  5,  4,  9 },			/*  4: KB fire	*/
#ifdef WINFF
    {  4,  6,  5, 10 },			/*  5: KB fire2	*/
#else
    {  4,  6,  5,  9 },			/*  5: KB fire2	*/
#endif
    {  5,  6,  6, 11 },			/*  6: Back	*/
    {  7,  8,  2,  7 },			/*  7: Joy num	*/
    {  7,  9,  3,  8 },			/*  8: Joy start*/
#ifdef WINFF
    {  8, 10,  4,  9 },			/*  9: Joy menu	*/
    {  9, 11,  5, 10 },			/* 10: Joy ff	*/
    { 10, 11,  6, 11 }			/* 11: Back	*/
#else
    {  8, 11,  4,  9 },			/*  9: Joy menu	*/
    {  0,  0,  0,  0 },			/* 10: Joy ff	*/
    {  9, 11,  6, 11 }			/* 11: Back2	*/
#endif
};

/*--------------------------------------------------------------*/
/* Keyboard.							*/
/*--------------------------------------------------------------*/

static void draw_keyboard_controls(KeyboardController *k, BITMAP *dest, int selected, int rn, int gn, int bn, int t)
{
#define KEY_TO_STRING(key)	key_to_string(key_name, key)
#define TEXTOUT_L(str,x,y,n)	[menu_font putString:str To:dest X:x Y:y Colour:PICK_RGB(n) Decoration:DECORATION_OUTLINE]
#define TEXTOUT_C(str,x,y,n)	[menu_font putString:str To:dest X:x Y:y Colour:PICK_RGB(n) Alignment:ALIGN_CENTRE Decoration:DECORATION_OUTLINE]

    char key_name[32];
    int rs, gs, bs;
    selected_item_colour(&rs, &gs, &bs, t);

    TEXTOUT_L("Fire:", 110, 250, -1);
    TEXTOUT_C(KEY_TO_STRING([k getKey:RAID_KEY_UP]),    200, 150, 0);
    TEXTOUT_C(KEY_TO_STRING([k getKey:RAID_KEY_LEFT]),  130, 180, 1);
    TEXTOUT_C(KEY_TO_STRING([k getKey:RAID_KEY_RIGHT]), 270, 180, 2);
    TEXTOUT_C(KEY_TO_STRING([k getKey:RAID_KEY_DOWN]),  200, 210, 3);
    TEXTOUT_L(KEY_TO_STRING([k getKey:RAID_KEY_FIRE]),  170, 250, 4);
    TEXTOUT_L(KEY_TO_STRING([k getKey:RAID_KEY_FIRE2]), 170, 280, 5);

#undef TEXTOUT_C
#undef TEXTOUT_L
#undef KEY_TO_STRING
}

static void mark_keyboard_controls_dirty(void)
{
    mark_dirty_rectangle(50, 150, 300, 310-150);
}

static BOOL grab_key(KeyboardController *k, int selected_item)
{
    int i, remapme;

    if (menu_released())
	return YES;

    switch (selected_item) {
      case 0: remapme = RAID_KEY_UP;    break;
      case 1: remapme = RAID_KEY_LEFT;  break;
      case 2: remapme = RAID_KEY_RIGHT; break;
      case 3: remapme = RAID_KEY_DOWN;  break;
      case 4: remapme = RAID_KEY_FIRE;  break;
      case 5: remapme = RAID_KEY_FIRE2; break;
      default: return YES;
    }

    for (i = 0; i < KEY_MAX; i++) {
	/* Reserved keys. */
	if (key[KEY_ENTER] || key[KEY_ENTER_PAD] || key[KEY_ESC])
	    continue;

	if (key[i]) {
	    [k setKey:remapme To:i];
	    return YES;
	}
    }

    return NO;
}

/*--------------------------------------------------------------*/
/* Joystick.							*/
/*--------------------------------------------------------------*/

static void draw_joystick_controls(JoystickController *j, BITMAP *dest, int selected, int rn, int gn, int bn, int t)
{
#define TEXTOUT_L(str,x,y,n)	[menu_font putString:str To:dest X:x Y:y Colour:PICK_RGB(n) Decoration:DECORATION_OUTLINE]
#define TEXTOUT_R(str,x,y,n)	[menu_font putString:str To:dest X:x Y:y Colour:PICK_RGB(n) Alignment:ALIGN_RIGHT Decoration:DECORATION_OUTLINE]

    int rs, gs, bs;
    selected_item_colour(&rs, &gs, &bs, t);

    TEXTOUT_R("Joystick ", 470, 190, JOY_NUM_ITEM);

    if (not j) {
	TEXTOUT_L("disabled", 480, 190, JOY_NUM_ITEM);
	/* Same as INACTIVE_COLOUR. */
	rn = 0xff-5*0x08, gn = 0xc0-5*0x10, bn = 0x60-5*0x10;
	TEXTOUT_L("N/A", 450, 220, -1);
	TEXTOUT_L("N/A", 450, 250, -1);
    }
    else {
	[menu_font putStringTo:dest X:480 Y:190 Colour:PICK_RGB(JOY_NUM_ITEM+0) Decoration:DECORATION_OUTLINE :"%d", [j joystickNumber]+1];
	[menu_font putStringTo:dest X:450 Y:220 Colour:PICK_RGB(JOY_NUM_ITEM+1) Decoration:DECORATION_OUTLINE :"Button %d", [j getKey:RAID_KEY_START]];
	[menu_font putStringTo:dest X:450 Y:250 Colour:PICK_RGB(JOY_NUM_ITEM+2) Decoration:DECORATION_OUTLINE :"Button %d", [j getKey:RAID_KEY_MENU]];
    }

    TEXTOUT_R("Start:",    440, 220, -1);
    TEXTOUT_R("Escape:",   440, 250, -1);

#ifdef WINFF
    TEXTOUT_L("Test Vibration", 390, 280, JOY_NUM_ITEM+3);
#endif

#undef TEXTOUT_V
#undef TEXTOUT_R
#undef TEXTOUT_L
}

static void mark_joystick_controls_dirty(void)
{
    int x1 = 440 - [menu_font textLength:"Escape:"];
    int x2 = 480 + [menu_font textLength:"disabled"];
    mark_dirty_rectangle(x1, 190, x2-x1+1, 280+[menu_font textHeight]-190);
}

static BOOL grab_joy(JoystickController *j, int selected_item)
{
    int b, remapme, joy_num;

    if (key[KEY_ESC])
	return YES;

    switch (selected_item) {
      case JOY_NUM_ITEM+1: remapme = RAID_KEY_START; break;
      case JOY_NUM_ITEM+2: remapme = RAID_KEY_MENU;  break;
      default: return YES;
    }

    joy_num = [j joystickNumber];
    for (b = 0; b < joy[joy_num].num_buttons; b++) {
	if (joy[joy_num].button[b].b) {
	    [j setKey:remapme To:b];
	    return YES;
	}
    }

    return NO;
}

/*--------------------------------------------------------------*/

static BOOL grab_input(int pl, int selected_item)
{
    if (selected_item <= EXIT_ITEM)
	return grab_key(player_controls[pl].keyboard, selected_item);
    else
	return grab_joy(player_controls[pl].joystick, selected_item);
}

static void process_okay(int pl, int selected_item, BOOL *begin_grabbing, BOOL *exit)
{
    if (selected_item == EXIT_ITEM || selected_item == EXIT_ITEM2)
	*exit = YES;
    elif (selected_item == JOY_NUM_ITEM) {
	int joy_num = 0;

	if (player_controls[pl].joystick)
	    joy_num = [player_controls[pl].joystick joystickNumber] + 1;

	/* After cycling through all joys, disable it. */
	if (joy_num >= num_joysticks)
	    player_controls[pl].joystick = nil;
	else
	    player_controls[pl].joystick = joystick_controller[joy_num];
    }

#ifdef WINFF
    elif (selected_item == 10)		/* Test vibration. */
	[player_controls[pl].joystick rumbleHurt];
#endif

    else
	*begin_grabbing = YES;
}

static void draw_back_button(BITMAP *dest, int selected_item, int t, BOOL grabbing)
{
#define TEXTOUT(str)		[menu_font putString:str To:dest X:dest->w/2 Y:dest->h-140 Colour:r:g:b Alignment:ALIGN_CENTRE Decoration:DECORATION_OUTLINE]

    int l, r, g, b;

    if (grabbing || selected_item == EXIT_ITEM || selected_item == EXIT_ITEM2)
	selected_item_colour(&r, &g, &b, t);
    else				/* Same as ACTIVE_COLOUR. */
	r = 0xff-1*0x08, g = 0xc0-1*0x10, b = 0x60-1*0x10;

    [menu_font setFontSize:MENU_FONT_SIZE];
    if (not grabbing)
	TEXTOUT("Back");
    elif (selected_item <= EXIT_ITEM)
	TEXTOUT("Press a Key");
    else
	TEXTOUT("Press a Button");

    l = [menu_font textLength:"Press a Button"];
    mark_dirty_rectangle((dest->w-l)/2, dest->h-140, l, [menu_font textHeight]);

#undef TEXTOUT
}

/*--------------------------------------------------------------*/
/* Key collisions.						*/
/*--------------------------------------------------------------*/

static BOOL check_clash(int key, KeyboardController *k)
{
    return (key == [k getKey:RAID_KEY_UP] ||
	    key == [k getKey:RAID_KEY_DOWN] ||
	    key == [k getKey:RAID_KEY_LEFT] ||
	    key == [k getKey:RAID_KEY_RIGHT] ||
	    key == [k getKey:RAID_KEY_FIRE] ||
	    key == [k getKey:RAID_KEY_FIRE2]);
}

static void fix_collisions(int pl, JoystickController *old_joy)
{
    int pp = !pl;		/* Other player. */
    KeyboardController *k  = player_controls[pl].keyboard;
    KeyboardController *kk = player_controls[pp].keyboard;

    /* Keyboard. */
    if (check_clash([k getKey:RAID_KEY_UP],    kk) ||
	check_clash([k getKey:RAID_KEY_DOWN],  kk) ||
	check_clash([k getKey:RAID_KEY_LEFT],  kk) ||
	check_clash([k getKey:RAID_KEY_RIGHT], kk) ||
	check_clash([k getKey:RAID_KEY_FIRE],  kk) ||
	check_clash([k getKey:RAID_KEY_FIRE2], kk))
	append_message("Warning: Key clashes detected.");
	
    /* Joystick. */
    if (player_controls[pl].joystick == player_controls[pp].joystick)
	player_controls[pp].joystick = old_joy;
}

/*--------------------------------------------------------------*/

static BOOL check_exit(int *selected_item)
{
    if (menu_released()) {
	if (*selected_item == EXIT_ITEM || *selected_item == EXIT_ITEM2) {
	    play_back_sound();
	    return YES;
	}
	elif (*selected_item <= EXIT_ITEM)
	    *selected_item = EXIT_ITEM;
	else
	    *selected_item = EXIT_ITEM2;
    }

    return NO;
}

void customise_controls_for_player(BITMAP *dbuf, int pl)
{
#define ACTIVE_COLOUR		0xff-1*0x08, 0xc0-1*0x10, 0x60-1*0x10
#define INACTIVE_COLOUR		0xff-5*0x08, 0xc0-5*0x10, 0x60-5*0x10

    JoystickController *old_joy = player_controls[pl].joystick;
    BOOL exit = NO, redraw = YES, grabbing = NO;
    int t = 0, wait_tics = 0;
    int selected_item = 0;
    Sebum<SebImage> *bg;

    bg = [menu_sebum getSebumByName:"controls"];
    menu_font = (SebFont *)[base_sebum getSebumByName:MENU_FONT_NAME];
    mark_entire_screen_dirty();
    
    while (not (exit || emergency_exit)) {
	while (counter) {
	    counter--;

	    t += 12;
	    if (t >= 360)
		t = 0;

	    menu_common_update();

	    if (grabbing) {
		if (grab_input(pl, selected_item)) {
		    grabbing = NO;
		    selected_item++;
		    wait_tics = 10;
		}
	    }
	    else {
		if (wait_tics)	/* Hack! */
		    wait_tics--;

		elif (check_exit(&selected_item))
		    exit = YES;
		elif (okay_released()) {
		    play_okay_sound();
		    process_okay(pl, selected_item, &grabbing, &exit);
		}

		{
		    int old_selected_item = selected_item;

		    if (menu_select_previous()) {
			if (selected_item == EXIT_ITEM2 && not player_controls[pl].joystick)
			    selected_item = JOY_NUM_ITEM;
			else
			    selected_item = menu_navigation_things[selected_item].up;
		    }
		    elif (menu_select_next()) {
			if (selected_item == JOY_NUM_ITEM && not player_controls[pl].joystick)
			    selected_item = EXIT_ITEM2;
			else
			    selected_item = menu_navigation_things[selected_item].down;
		    }
		    elif (menu_select_left())
			selected_item = menu_navigation_things[selected_item].left;
		    elif (menu_select_right()) {
			selected_item = menu_navigation_things[selected_item].right;

			if (selected_item >= JOY_NUM_ITEM && not player_controls[pl].joystick) {
			    if (selected_item <= JOY_NUM_ITEM+2)
				selected_item = JOY_NUM_ITEM;
			    else
				selected_item = EXIT_ITEM2;
			}
		    }

		    if (old_selected_item <= EXIT_ITEM && selected_item > EXIT_ITEM)
			mark_keyboard_controls_dirty();
		    else if (old_selected_item > EXIT_ITEM && selected_item <= EXIT_ITEM)
			mark_joystick_controls_dirty();

		    if (selected_item != old_selected_item)
			play_bing_sound();
		}
	    }

	    redraw = YES;
	    rest(0);
	}


	if (redraw) {
	    redraw = NO;

	    [menu_font setFontSize:20];
	    [bg drawTo:dbuf X:(dbuf->w-[bg width])/2 Y:(dbuf->h-[bg height])/2 W:dbuf->w H:dbuf->h];

	    if (selected_item <= EXIT_ITEM) {
		draw_keyboard_controls(player_controls[pl].keyboard, dbuf, selected_item, ACTIVE_COLOUR, t);
		draw_joystick_controls(player_controls[pl].joystick, dbuf, selected_item, INACTIVE_COLOUR, t);
		mark_keyboard_controls_dirty();
	    }
	    else {
		draw_keyboard_controls(player_controls[pl].keyboard, dbuf, selected_item, INACTIVE_COLOUR, t);
		draw_joystick_controls(player_controls[pl].joystick, dbuf, selected_item, ACTIVE_COLOUR, t);
		mark_joystick_controls_dirty();
	    }

	    draw_back_button(dbuf, selected_item, t, grabbing);
	    blit_dirty_rectangles(dbuf, screen);
	}
    }

    fix_collisions(pl, old_joy);

    /* Restore the font size for the main menu. */
    [menu_font setFontSize:MENU_FONT_SIZE];

#undef INACTIVE_COLOUR
#undef ACTIVE_COLOUR
}
