#include "allegro.h"

#include "ControlConfig.h"
#include "InputObj.h"
#include "GameSettings.h"
#include "tasmMath.h"

#include "gameConstants.h"
#include <string>
#include <vector>
using namespace std;

char *key_names[] =
{
   "(none)",         "A",          "B",          "C",
   "D",          "E",          "F",          "G",
   "H",          "I",          "J",          "K",
   "L",          "M",          "N",          "O",
   "P",          "Q",          "R",          "S",
   "T",          "U",          "V",          "W",
   "X",          "Y",          "Z",          "0",
   "1",          "2",          "3",          "4",
   "5",          "6",          "7",          "8",
   "9",          "0_PAD",      "1_PAD",      "2_PAD",
   "3_PAD",      "4_PAD",      "5_PAD",      "6_PAD",
   "7_PAD",      "8_PAD",      "9_PAD",      "F1",
   "F2",         "F3",         "F4",         "F5",
   "F6",         "F7",         "F8",         "F9",
   "F10",        "F11",        "F12",        "ESC",
   "TILDE",      "MINUS",      "EQUALS",     "BACKSPACE",
   "TAB",        "OPENBRACE",  "CLOSEBRACE", "ENTER",
   "COLON",      "QUOTE",      "BACKSLASH",  "BACKSLASH2",
   "COMMA",      "STOP",       "SLASH",      "SPACE",
   "INSERT",     "DEL",        "HOME",       "END",
   "PGUP",       "PGDN",       "LEFT",       "RIGHT",
   "UP",         "DOWN",       "SLASH_PAD",  "ASTERISK",
   "MINUS_PAD",  "PLUS_PAD",   "DEL_PAD",    "ENTER_PAD",
   "PRTSCR",     "PAUSE",      "ABNT_C1",    "YEN",
   "KANA",       "CONVERT",    "NOCONVERT",  "AT",
   "CIRCUMFLEX", "COLON2",     "KANJI",      "EQUALS_PAD",
   "BACKQUOTE",  "SEMICOLON",  "COMMAND",    "UNKNOWN1",
   "UNKNOWN2",   "UNKNOWN3",   "UNKNOWN4",   "UNKNOWN5",
   "UNKNOWN6",   "UNKNOWN7",   "UNKNOWN8",   "LSHIFT",
   "RSHIFT",     "LCONTROL",   "RCONTROL",   "ALT",
   "ALTGR",      "LWIN",       "RWIN",       "MENU",
   "SCRLOCK",    "NUMLOCK",    "CAPSLOCK",   "MAX"
};

int ControlConfig :: ControlBgColorR = 183;
int ControlConfig :: ControlBgColorB = 249;
int ControlConfig :: ControlBgColorG = 179;

int ControlConfig :: ControlFgColorR = 0;
int ControlConfig :: ControlFgColorB = 0;
int ControlConfig :: ControlFgColorG = 0;

string ControlConfig :: mouseThresholdDesc ("HTUH");
string ControlConfig :: mouseThresholdDesc2 ("HTUH");
fixed ControlConfig :: drawAngle = ftofix (0.0);
volatile int ControlConfig :: tickPassed = 0;
BITMAP * ControlConfig :: thresholdTemp = NULL;
int ControlConfig :: thresholdColor;
int ControlConfig :: thresholdTimer;

InputObj * ControlConfig :: controlConfigIO = NULL;
GameSettings * ControlConfig :: controlGS = NULL;
int ControlConfig :: keyRefIndex = 3;
int ControlConfig :: mouseRefIndex = 26;

DIALOG ControlConfig :: controlConfigDialog [] = 
  {
    /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */
    { d_clear_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
    { my_button_proc, 100, 285, 200, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Main Menu", NULL, (void *)toMain },

    { d_box_proc, 0, 0, 200, 245, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    //3
    { my_button_proc, 10, 20, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Up", NULL, (void *)changeButtonUp },
    { d_text_proc, 115, 25, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 45, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Down", NULL, (void *)changeButtonDown },
    { d_text_proc, 115, 50, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 70, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Left", NULL, (void *)changeButtonLeft },
    { d_text_proc, 115, 75, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 95, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Right", NULL, (void *)changeButtonRight },
    { d_text_proc, 115, 100, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 120, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Hyper", NULL, (void *)changeButtonHyper },
    { d_text_proc, 115, 125, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 145, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Restart", NULL, (void *)changeButtonRestart },
    { d_text_proc, 115, 150, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 170, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Pause", NULL, (void *)changeButtonPause },
    { d_text_proc, 115, 175, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 195, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Menu", NULL, (void *)changeButtonMenu },
    { d_text_proc, 115, 200, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { my_button_proc, 10, 220, 80, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Taunt", NULL, (void *)changeButtonTaunt },
    { d_text_proc, 115, 225, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
    
    { my_button_proc, 100, 260, 200, 20, 0, 0, 0, D_EXIT, 0, 0, (char *)"Default Controls", NULL, (void *)defaultControls },

    { d_text_proc, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, (char *)"Keyboard", NULL, NULL },

    { d_box_proc, 200, 0, 200, 245, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { d_text_proc, 205, 5, 0, 0, 0, 0, 0, 0, 0, 0, (char *)"Mouse", NULL, NULL },
    { d_text_proc, 205, 25, 0, 0, 0, 0, 0, 0, 0, 0, (char *)"Sensitivity", NULL, NULL },

    //mouseRefIndex = 26
    { d_slider_proc, 205, 40, 160, 20, 0, 0, 0, 0, 100, 0, NULL, (void *)updateThresholdSlider, NULL },

    { d_rtext_proc, 395, 45, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { mouse_proc, 225, 60, 150, 150, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL},

    { d_text_proc, 220, 220, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },

    { d_yield_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL},
    { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL  }
  };

int ControlConfig :: toMain ()
{ 
  //save to disk

  controlGS -> saveControls (controlConfigIO);
  return D_CLOSE;
}

void ControlConfig :: incTick()
{
  tickPassed++;
}
END_OF_FUNCTION(ControlConfig :: incTick);

//key config
int ControlConfig :: changeButtonUp ()
{ return changeButton(keyUp);}

int ControlConfig :: changeButtonDown ()
{ return changeButton(keyDown);}

int ControlConfig :: changeButtonLeft ()
{ return changeButton(keyLeft);}

int ControlConfig :: changeButtonRight ()
{ return changeButton(keyRight);}

int ControlConfig :: changeButtonHyper ()
{ return changeButton(keyHyper);}

int ControlConfig :: changeButtonMenu ()
{ return changeButton(keyMenu);}

int ControlConfig :: changeButtonRestart ()
{ return changeButton(keyRestart);}

int ControlConfig :: changeButtonPause ()
{ return changeButton(keyPause);}

int ControlConfig :: changeButtonTaunt ()
{ return changeButton(keyTaunt);}

int ControlConfig :: changeButton(int keyIndex)
{
  int readkeyReturn;

  //capture next keypress
  if (screen != NULL)
    {
      rectfill (screen, SCREEN_W/2 - 60, SCREEN_H/2 - 10, SCREEN_W/2 + 60, SCREEN_H/2 + 10, makecol (255, 255, 255));

      rect (screen, SCREEN_W/2 - 60, SCREEN_H/2 - 10, SCREEN_W/2 + 60, SCREEN_H/2 + 10, makecol (0, 0, 0));
      
      textout_centre_ex(screen, font, "Press any key", SCREEN_W/2, SCREEN_H/2 - 5, makecol (0, 0, 0), makecol (255, 255, 255));
    }

  readkeyReturn = readkey ();

  controlConfigDialog [keyRefIndex + 1 + (2*keyIndex)].dp = key_names[readkeyReturn>>8];
  controlConfigIO -> setKeyMapping (keyIndex, readkeyReturn>>8);

  dialog_message (controlConfigDialog, MSG_DRAW, 0, NULL);
  return D_O_K;
}

int ControlConfig :: defaultControls ()
{
  controlConfigDialog [keyRefIndex + 1 + (2*keyUp)].dp = key_names [keyBindUp];
  controlConfigIO -> setKeyMapping (keyUp, keyBindUp);

  controlConfigDialog [keyRefIndex + 1 + (2*keyDown)].dp = key_names [keyBindDown];
  controlConfigIO -> setKeyMapping (keyDown, keyBindDown);

  controlConfigDialog [keyRefIndex + 1 + (2*keyLeft)].dp = key_names [keyBindLeft];
  controlConfigIO -> setKeyMapping (keyLeft, keyBindLeft);

  controlConfigDialog [keyRefIndex + 1 + (2*keyRight)].dp = key_names [keyBindRight];
  controlConfigIO -> setKeyMapping (keyRight, keyBindRight);

  controlConfigDialog [keyRefIndex + 1 + (2*keyHyper)].dp = key_names [keyBindHyper];
  controlConfigIO -> setKeyMapping (keyHyper, keyBindHyper);

  controlConfigDialog [keyRefIndex + 1 + (2*keyRestart)].dp = key_names [keyBindRestart];
  controlConfigIO -> setKeyMapping (keyRestart, keyBindRestart);

  controlConfigDialog [keyRefIndex + 1 + (2*keyPause)].dp = key_names [keyBindPause];
  controlConfigIO -> setKeyMapping (keyPause, keyBindPause);

  controlConfigDialog [keyRefIndex + 1 + (2*keyMenu)].dp = key_names [keyBindMenu];
  controlConfigIO -> setKeyMapping (keyMenu, keyBindMenu);

  controlConfigDialog [keyRefIndex + 1 + (2*keyTaunt)].dp = key_names [keyBindTaunt];
  controlConfigIO -> setKeyMapping (keyTaunt, keyBindTaunt);

  controlConfigIO -> setMouseControlThreshold (10);
  controlConfigDialog [mouseRefIndex].d2 = 10;
  updateThresholdSlider (NULL, 10);

  dialog_message (controlConfigDialog, MSG_DRAW, 0, NULL);
  return D_O_K;
}

int ControlConfig :: updateThresholdSlider (void *dp3, int d2)
{
  char tempSter [80];
 
  if (d2 < 100)
    sprintf (tempSter, " %d", d2);
  else
    sprintf (tempSter, "%d", d2);

  mouseThresholdDesc.assign (tempSter);
  controlConfigDialog [mouseRefIndex + 1].dp = (void *) mouseThresholdDesc.c_str ();

  dialog_message (&(controlConfigDialog[mouseRefIndex + 1]), MSG_DRAW, 0, NULL);
  show_mouse (screen);

  controlConfigIO -> setMouseControlThreshold (d2);

  return D_O_K;
}

int ControlConfig :: mouse_proc(int msg, DIALOG *d, int c)
{
  int cursorX = 0, cursorY = 0;  
  fixed micX = itofix (0), micY = itofix (0), micDist = itofix (0), micAngle = itofix (0), turnDist = itofix (0);
  int ct = controlConfigDialog[mouseRefIndex].d2;
  fixed curDrawX, curDrawY;
  int bkCol;
  
  switch (msg) {
  case MSG_START:
    break;
  case MSG_END:
    break;
  case MSG_IDLE:
    if (tickPassed > 0)
      {
	tickPassed = 0;
	thresholdTimer--;

	get_mouse_mickeys (&cursorX, &cursorY);

	//all taken from aim in Player.h

	//transformation for screen vs cartesian coordinates
	micX = itofix (-1 * cursorX);
	micY = itofix (cursorY);
	micDist = fixhypot (micX, micY);

	if (micDist > itofix (1))
	  {
	    if (cursorY == 0 && cursorX < 0)
	      micAngle = itofix (-64);
	    else if (cursorY == 0  && cursorX > 0)
	      micAngle = itofix (64);
	    else
	      micAngle = fixatan (fixdiv (micX, micY));

	    if (micY > itofix (0))
	      micAngle -= itofix (128);

	    micAngle = micAngle ^ 256;
      
	    if (micDist > itofix (ct))
	      {
		drawAngle = micAngle;
		thresholdColor = makecol (255, 255, 255);
		thresholdTimer = 5;

		mouseThresholdDesc2.assign ("FAST: Direct Aiming");
		controlConfigDialog [mouseRefIndex + 3].dp = (void *) mouseThresholdDesc2.c_str ();
		object_message(&(controlConfigDialog [mouseRefIndex + 3]), MSG_DRAW, 0);
	      }
	    else
	      {
		turnDist = angleDist (micAngle, drawAngle);
		drawAngle += turnDist / 10;
		if (thresholdTimer <= 0)
		  { thresholdColor = makecol (0, 0, 0);
		  mouseThresholdDesc2.assign ("SLOW: Turn Aiming  ");
		  controlConfigDialog [mouseRefIndex + 3].dp = (void *) mouseThresholdDesc2.c_str (); 
		  object_message(&(controlConfigDialog [mouseRefIndex + 3]), MSG_DRAW, 0);
		  }
	      }
	  }
	//now redraw the image
	object_message(d, MSG_DRAW, 0);
      }  
    break;
  case MSG_DRAW:
    //draw here
    bkCol = makecol (192,192,192);
    clear_to_color (thresholdTemp, bkCol);

    curDrawX = itofix(d->w/2) + (d->w/3)*fixsin(drawAngle);
    curDrawY = itofix(d->h/2) - (d->w/3)*fixcos(drawAngle);

    line (thresholdTemp, d->w/2, d->h/2, fixtoi(curDrawX), fixtoi(curDrawY), thresholdColor);

    blit(thresholdTemp, screen, 0, 0, d->x, d->y, d->w, d->h);
    break;
  }

  return D_O_K;
}

ControlConfig :: ControlConfig (InputObj * inpOb, GameSettings * gset)
{
  LOCK_VARIABLE(ControlConfig :: tickPassed);
  LOCK_FUNCTION(ControlConfig :: incTick);
  install_int_ex(ControlConfig :: incTick, MSEC_TO_TIMER(30));
  thresholdTemp = create_bitmap (controlConfigDialog [mouseRefIndex + 2].w, controlConfigDialog[mouseRefIndex + 2].h);
      
  controlConfigIO = inpOb;

  for (int i = 0; i < keyNumberOfKeys; i ++)
    {
      controlConfigDialog [keyRefIndex + 1 + (2*i)].dp = key_names [controlConfigIO -> getKeyMappings () -> at (i)];
    }

  controlGS = gset;

  char tempSter [80];
  sprintf (tempSter, "%d", controlConfigIO -> getMouseControlThreshold ());
  mouseThresholdDesc.assign (tempSter);
  controlConfigDialog [mouseRefIndex + 1].dp = (void *) mouseThresholdDesc.c_str ();
  controlConfigDialog [mouseRefIndex].d2 = controlConfigIO -> getMouseControlThreshold ();

  mouseThresholdDesc2.assign ("Move Mouse");
  controlConfigDialog [mouseRefIndex + 3].dp = (void *) mouseThresholdDesc2.c_str ();
}

void ControlConfig :: go (int index)
{
  for (int i = 0; i < keyNumberOfKeys; i ++)
    { controlConfigDialog [keyRefIndex + 1 + (2*i)].dp = key_names [controlConfigIO -> getKeyMappings () -> at (i)]; }

  ControlRedrawMenu ();
  show_mouse (screen);
  do_dialog (controlConfigDialog, index);
}

ControlConfig :: ~ControlConfig ()
{ destroy_bitmap (thresholdTemp); }

void ControlConfig :: ControlRedrawMenu ()
{
  set_dialog_color (controlConfigDialog, 
		    makecol (ControlFgColorR, ControlFgColorB, ControlFgColorG),
		    makecol (ControlBgColorR, ControlBgColorB, ControlBgColorG));
  
  centre_dialog (controlConfigDialog);
  dialog_message (controlConfigDialog, MSG_DRAW, 0, NULL); 
}

int ControlConfig :: my_button_proc (int msg, DIALOG *d, int c)
{
   int ret = d_button_proc (msg, d, c);
   if (ret == D_CLOSE && d->dp3)
      return ((int (*)(void))d->dp3)();
   return ret;
}
