/* FCE Ultra - NES/Famicom Emulator
 *
 * Copyright notice for this file:
 *  Copyright (C) 2002 Ben Parnell
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../../driver.h"
#include "../common/cheat.h"

#include "main.h"
#include "input.h"
#include "sb.h"
#include "video.h"
#include "keyboard.h"
#include "mouse.h"
#include "joystick.h"

static void UpdateFKB(void);
static int gametype;
int UsrInputType[2]={SI_GAMEPAD,SI_GAMEPAD};
int UsrInputTypeFC=SIFC_NONE;
static int InputType[2];
static int InputTypeFC;

void ParseGI(FCEUGI *gi)
{
 gametype=gi->type;

 InputType[0]=UsrInputType[0];
 InputType[1]=UsrInputType[1];
 InputTypeFC=UsrInputTypeFC;

 if(gi->input[0]>=0)
  InputType[0]=gi->input[0];
 if(gi->input[1]>=0)
  InputType[1]=gi->input[1];
 if(gi->inputfc>=0)
  InputTypeFC=gi->inputfc;

 FCEUI_GetCurrentVidSystem(&srendline,&erendline);
}

static uint32 JSreturn;
int NoWaiting=0;

static void UpdateMouse(void);


int InitKeyboard(void)
{
  if(!keyboard_init()) 
  {
   puts("Error initializing keyboard.");
   return 0;
  }
  return 1;
}

static void DoCheatSeq(void)
{
 SilenceSound(1);
 keyboard_close();
 KillVideo();
 DoConsoleCheatConfig();
 InitVideo();
 InitKeyboard();
 SilenceSound(0);
}

static char *keys=0;
static int DIPS=0;
static uint8 keyonce[128];
#define KEY(__a) keys[SCAN_##__a]
#define keyonly(__a,__z) {if(KEY(__a)){if(!keyonce[SCAN_##__a]) {keyonce[SCAN_##__a]=1;__z}}else{keyonce[SCAN_##__a]=0;}}
static int JoySwap=0;
static int cidisabled=0;
static int KeyboardUpdate(void)
{
  if(!keyboard_update())
   if(keys)
    return 0;

  keys=keyboard_getstate();

  if(InputTypeFC==SIFC_FKB)
  {
   keyonly(SCROLLLOCK,cidisabled^=1;
    FCEUI_DispMessage("Family Keyboard %sabled.",cidisabled?"en":"dis");)
   if(cidisabled) return(1);
  }

  NoWaiting=0;
  if(KEY(GRAVE))
   NoWaiting=1;

  if(gametype==GIT_FDS)
  {
   keyonly(S,DriverInterface(DES_FDSSELECT,0);)
   keyonly(I,DriverInterface(DES_FDSINSERT,0);)
   keyonly(E,DriverInterface(DES_FDSEJECT,0);)
  }
 if(gametype!=GIT_NSF)
 {
  keyonly(F2,DoCheatSeq();)
  keyonly(F5,FCEUI_SaveState();)
  keyonly(F7,FCEUI_LoadState();)
  keyonly(F9,FCEUI_SaveSnapshot();)
 }
 else
 {
  keyonly(CURSORLEFT,DriverInterface(DES_NSFDEC,0);)
  keyonly(CURSORRIGHT,DriverInterface(DES_NSFINC,0);)
  if( KEY(ENTER)) DriverInterface(DES_NSFRES,0);
  if( KEY(CURSORUP)) DriverInterface(DES_NSFINC,0);
  if( KEY(CURSORDOWN)) DriverInterface(DES_NSFDEC,0);
 }

 keyonly(F10,DriverInterface(DES_RESET,0);)
 keyonly(F11,DriverInterface(DES_POWER,0);)
 if(KEY(F12) || KEY(ESCAPE)) FCEUI_CloseGame();

 if(gametype==GIT_VSUNI)
 {
   keyonly(C,DriverInterface(DES_VSUNICOIN,0);)
   keyonly(V,DIPS^=1;DriverInterface(DES_VSUNITOGGLEDIPVIEW,0);)
   if(!(DIPS&1)) goto DIPSless;
   keyonly(1,DriverInterface(DES_VSUNIDIPSET,(void *)1);)
   keyonly(2,DriverInterface(DES_VSUNIDIPSET,(void *)2);)
   keyonly(3,DriverInterface(DES_VSUNIDIPSET,(void *)3);)
   keyonly(4,DriverInterface(DES_VSUNIDIPSET,(void *)4);)
   keyonly(5,DriverInterface(DES_VSUNIDIPSET,(void *)5);)
   keyonly(6,DriverInterface(DES_VSUNIDIPSET,(void *)6);)
   keyonly(7,DriverInterface(DES_VSUNIDIPSET,(void *)7);)
   keyonly(8,DriverInterface(DES_VSUNIDIPSET,(void *)8);)
 }
 else
 {
  keyonly(H,DriverInterface(DES_NTSCSELHUE,0);)
  keyonly(T,DriverInterface(DES_NTSCSELTINT,0);)
  if(KEY(KP_MINUS) || KEY(MINUS)) DriverInterface(DES_NTSCDEC,0);
  if(KEY(KP_PLUS) || KEY(EQUAL)) DriverInterface(DES_NTSCINC,0);

  DIPSless:
  keyonly(0,FCEUI_SelectState(0);)
  keyonly(1,FCEUI_SelectState(1);)
  keyonly(2,FCEUI_SelectState(2);)
  keyonly(3,FCEUI_SelectState(3);)
  keyonly(4,FCEUI_SelectState(4);)
  keyonly(5,FCEUI_SelectState(5);)
  keyonly(6,FCEUI_SelectState(6);)
  keyonly(7,FCEUI_SelectState(7);)
  keyonly(8,FCEUI_SelectState(8);)
  keyonly(9,FCEUI_SelectState(9);)
 }
 return 1;
}

static uint32 KeyboardDodo(void)
{
 uint32 JS=0;

 if(gametype!=GIT_NSF)
 {
  int x,y;
  x=y=0;
  keyonly(CAPSLOCK,
                   {
                    char tmp[64];
                    JoySwap=(JoySwap+8)%32;
                    sprintf(tmp,"Joystick %d selected.",(JoySwap>>3)+1);
                    FCEUI_DispMessage(tmp);
                   })

  if(KEY(LEFTALT) || KEY(X))        JS|=JOY_A<<JoySwap;
  if(KEY(LEFTCONTROL) || KEY(SPACE) || KEY(Z) ) JS |=JOY_B<<JoySwap;
  if(KEY(ENTER))       JS |= JOY_START<<JoySwap;
  if(KEY(TAB))         JS |= JOY_SELECT<<JoySwap;

  if(KEY(HOME)) {y|=JOY_UP; x|=JOY_LEFT;}
  if(KEY(END)) {y|=JOY_DOWN; x|=JOY_LEFT;}
  if(KEY(PAGEUP)) {y|=JOY_UP; x|=JOY_RIGHT;}
  if(KEY(PAGEDOWN)) {y|=JOY_DOWN; x|=JOY_RIGHT;}

  if(KEY(CURSORDOWN))  y|= JOY_DOWN;
  if(KEY(CURSORUP))    y|= JOY_UP;
  if(KEY(CURSORLEFT))  x|= JOY_LEFT;
  if(KEY(CURSORRIGHT)) x|= JOY_RIGHT;
  if(y!=(JOY_DOWN|JOY_UP)) JS|=y<<JoySwap;
  if(x!=(JOY_LEFT|JOY_RIGHT)) JS|=x<<JoySwap;
 }
 return JS;
}

static int powerpadsc[2][12]={
                              {
                               SCAN_O,SCAN_P,SCAN_BRACKET_LEFT,
                               SCAN_BRACKET_RIGHT,SCAN_K,SCAN_L,SCAN_SEMICOLON,SCAN_APOSTROPHE,
                               SCAN_M,SCAN_COMMA,SCAN_PERIOD,SCAN_SLASH
                              },
                              {
                               SCAN_O,SCAN_P,SCAN_BRACKET_LEFT,
                               SCAN_BRACKET_RIGHT,SCAN_K,SCAN_L,SCAN_SEMICOLON,SCAN_APOSTROPHE,
                               SCAN_M,SCAN_COMMA,SCAN_PERIOD,SCAN_SLASH
                              }
                             };

uint32 powerpadbuf[2];
int powerpadside=0;

static uint32 UpdatePPadData(int w)
{
 static const char shifttableA[12]={8,9,0,1,11,7,4,2,10,6,5,3};
 static const char shifttableB[12]={1,0,9,8,2,4,7,11,3,5,6,10};
 uint32 r=0;
 int *ppadtsc=powerpadsc[w];
 int x;

 if(powerpadside&(1<<w))
 {
  for(x=0;x<12;x++)
   if(keys[ppadtsc[x]]) r|=1<<shifttableA[x];
 }
 else
 {
  for(x=0;x<12;x++)
   if(keys[ppadtsc[x]]) r|=1<<shifttableB[x];
 }
 return r;
}

void FCEUD_UpdateInput(void)
{
  int x;
  int t=0;
  static uint32 KeyBJS=0;
  uint32 JS;
  int b;

  b=KeyboardUpdate();

  for(x=0;x<2;x++)
   switch(InputType[x])
   {
    case SI_GAMEPAD:t|=1;break;
    case SI_ZAPPER:t|=2;break;
    case SI_ARKANOID:t|=2;break;
    case SI_POWERPAD:powerpadbuf[x]=UpdatePPadData(x);break;
   }

  switch(InputTypeFC)
  {
   case SIFC_ARKANOID:t|=2;break;
   case SIFC_SHADOW:t|=2;break;
   case SIFC_FKB:if(cidisabled) UpdateFKB();break;
  }


  if(t&1)
  {
   if(b)
    KeyBJS=KeyboardDodo();
   JS=KeyBJS;

   if(joy)
    JS|=(uint32)GetJSOr();

   JSreturn=(JS&0xFF000000)|(JS&0xFF)|((JS&0xFF0000)>>8)|((JS&0xFF00)<<8);
  }
  if(t&2)
   UpdateMouse();
}

static uint8 fkbkeys[0x48];
static uint32 MouseData[3];
void InitOtherInput(void)
{
   void *InputDPtr;
   int t;
   int x;
   int attrib;

   for(t=0,x=0;x<2;x++)
   {
    attrib=0;
    InputDPtr=0;
    switch(InputType[x])
    {
     case SI_POWERPAD:InputDPtr=&powerpadbuf[x];break;
     case SI_GAMEPAD:InputDPtr=((uint8 *)&JSreturn)+(x<<1);break;
     case SI_ARKANOID:InputDPtr=MouseData;t|=1;break;
     case SI_ZAPPER:InputDPtr=MouseData;                     
                                t|=1;
                                attrib=1;
                                break;
    }
    FCEUI_SetInput(x,InputType[x],InputDPtr,attrib);
   }

   attrib=0;
   InputDPtr=0;
   switch(InputTypeFC)
   {
    case SIFC_SHADOW:InputDPtr=MouseData;t|=1;attrib=1;break;
    case SIFC_ARKANOID:InputDPtr=MouseData;t|=1;break;
    case SIFC_FKB:InputDPtr=fkbkeys;memset(fkbkeys,0,sizeof(fkbkeys));break;
   }

   FCEUI_SetInputFC(InputTypeFC,InputDPtr,attrib);
   FCEUI_DisableFourScore(eoptions&EO_NOFOURSCORE);

   if(t)
    InitMouse();
}

static void UpdateMouse(void)
{
 MouseData[2]=GetMouseData(&MouseData[0], &MouseData[1]);
}

int fkbmap[0x48]=
{
 SCAN_F1,SCAN_F2,SCAN_F3,SCAN_F4,SCAN_F5,SCAN_F6,SCAN_F7,SCAN_F8,
 SCAN_1,SCAN_2,SCAN_3,SCAN_4,SCAN_5,SCAN_6,SCAN_7,SCAN_8,SCAN_9,SCAN_0,
        SCAN_MINUS,SCAN_EQUAL,SCAN_BACKSLASH,SCAN_BACKSPACE,
 SCAN_ESCAPE,SCAN_Q,SCAN_W,SCAN_E,SCAN_R,SCAN_T,SCAN_Y,SCAN_U,SCAN_I,SCAN_O,
        SCAN_P,SCAN_GRAVE,SCAN_BRACKET_LEFT,SCAN_ENTER,
 SCAN_LEFTCONTROL,SCAN_A,SCAN_S,SCAN_D,SCAN_F,SCAN_G,SCAN_H,SCAN_J,SCAN_K,
        SCAN_L,SCAN_SEMICOLON,SCAN_APOSTROPHE,SCAN_BRACKET_RIGHT,SCAN_BL_INSERT,
 SCAN_LEFTSHIFT,SCAN_Z,SCAN_X,SCAN_C,SCAN_V,SCAN_B,SCAN_N,SCAN_M,SCAN_COMMA,
        SCAN_PERIOD,SCAN_SLASH,SCAN_RIGHTALT,SCAN_RIGHTSHIFT,SCAN_LEFTALT,SCAN_SPACE,
 SCAN_BL_DELETE,SCAN_BL_END,SCAN_BL_PAGEDOWN,
 SCAN_BL_CURSORUP,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT,SCAN_BL_CURSORDOWN
};

static void UpdateFKB(void)
{
 int x;

 for(x=0;x<0x48;x++)
 {
  fkbkeys[x]=0;
  if(keys[fkbmap[x]])
   fkbkeys[x]=1;
 }
}


