/* FCE Ultra - NES/Famicom Emulator
 *
 * Copyright notice for this file:
 *  Copyright (C) 2002 Xodnizel
 *
 * 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 "common.h"
#include <dinput.h>

#include "input.h"
#include "keyboard.h"
#include "joystick.h"

LPDIRECTINPUT7 lpDI;

/* UsrInputType[] is user-specified.  InputType[] is current
        (game loading can override user settings)
*/

int UsrInputType[3]={SI_GAMEPAD,SI_GAMEPAD,SIFC_NONE};
int InputType[3];
static int cspec;

int gametype;

int InitDInput(void)
{
 HRESULT ddrval;

 ddrval=DirectInputCreateEx(fceu_hInstance,DIRECTINPUT_VERSION,&IID_IDirectInput7,(LPVOID *)&lpDI,0);
 if(ddrval!=DI_OK)
 {
  FCEUD_PrintError("DirectInput: Error creating DirectInput object.");
  return 0;
 }
 return 1;
}

static uint32 MouseData[3];
static int screenmode=0;
void InputScreenChanged(int fs)
{
 int x;
 if(GI)
 {
  for(x=0;x<2;x++)
   if(InputType[x]==SI_ZAPPER)
    FCEUI_SetInput(x,SI_ZAPPER,MouseData,fs);
  if(InputType[2]==SIFC_SHADOW)
   FCEUI_SetInputFC(SIFC_SHADOW,MouseData,fs);
 }
 screenmode=fs;
}

/* Necessary for proper GUI functioning(configuring when a game isn't loaded). */
void InputUserActiveFix(void)
{
 int x;
 for(x=0;x<3;x++) InputType[x]=UsrInputType[x];
}

void ParseGIInput(FCEUGI *gi)
{
 InputType[0]=UsrInputType[0];
 InputType[1]=UsrInputType[1];
 InputType[2]=UsrInputType[2];

 if(gi)
 {
  if(gi->input[0]>=0)
   InputType[0]=gi->input[0];
  if(gi->input[1]>=0)
   InputType[1]=gi->input[1];
  if(gi->inputfc>=0)
   InputType[2]=gi->inputfc;
  cspec = gi->cspecial;
  gametype=gi->type;

  InitOtherInput();
 }
 else cspec=gametype=0;
}


static uint8 QuizKingData;
static uint8 HyperShotData;
static uint32 MahjongData;
static uint32 FTrainerData;
static uint8 TopRiderData;

static uint8 BWorldData[1+13+1];

static void UpdateFKB(void);
static void UpdateSuborKB(void);
static void UpdateGamepad(void);
static void UpdateQuizKing(void);
static void UpdateHyperShot(void);
static void UpdateMahjong(void);
static void UpdateFTrainer(void);
static void UpdateTopRider(void);

static uint32 JSreturn;
int NoWaiting=1;

#include "keyscan.h"
static char *keys;
static int DIPS=0;

static uint8 keyonce[MKK_COUNT];
#define KEY(__a) keys[MKK(__a)]

static int _keyonly(int a)
{
 if(keys[a])
 {
  if(!keyonce[a])
  {
   keyonce[a]=1;
   return(1);
  }
 }
 else
  keyonce[a]=0;
 return(0);
}

#define keyonly(__a) _keyonly(MKK(__a))

int cidisabled=0;

static void KeyboardCommands(void)
{
  int is_shift, is_alt;

  keys=GetKeyboard();

  if((InputType[2]==SIFC_FKB)||(InputType[2]==SIFC_SUBORKB))
  {
   if(cidisabled) return;
  }

  is_shift = KEY(LEFTSHIFT) | KEY(RIGHTSHIFT);
  is_alt = KEY(LEFTALT) | KEY(RIGHTALT);

  if(keyonly(F4))
  {
   if(is_shift) FCEUI_SetRenderDisable(-1, 2);
   else FCEUI_SetRenderDisable(2, -1);
  }

  {
   static int nocount=0;

   NoWaiting&=~1;
   if(KEY(GRAVE)) //&& nocount)
    NoWaiting|=1;
   nocount=(nocount+1)&3;
  }

  if(gametype==GIT_FDS)
  {
   if(keyonly(F6)) FCEUI_FDSSelect();
   if(keyonly(F8)) FCEUI_FDSInsert(0);
  }

  if(keyonly(F9)) FCEUI_SaveSnapshot();
  if(gametype!=GIT_NSF)
  {
   if(keyonly(F5))
   {
    if(is_shift)
     FCEUI_SaveMovie(NULL);
    else
     FCEUI_SaveState(NULL);
   }
   if(keyonly(F7))
   {
    if(is_shift)
     FCEUI_LoadMovie(NULL);
    else
     FCEUI_LoadState(NULL);
   }
  }

//  if(keyonly(F1)) FCEUI_ToggleTileView();

  if(gametype==GIT_VSUNI)
  {
        if(keyonly(F8)) FCEUI_VSUniCoin();
        if(keyonly(F6))
        {
         DIPS^=1;
         FCEUI_VSUniToggleDIPView();
        }
        if(!(DIPS&1)) goto DIPSless;
        if(keyonly(1)) FCEUI_VSUniToggleDIP(0);
        if(keyonly(2)) FCEUI_VSUniToggleDIP(1);
        if(keyonly(3)) FCEUI_VSUniToggleDIP(2);
        if(keyonly(4)) FCEUI_VSUniToggleDIP(3);
        if(keyonly(5)) FCEUI_VSUniToggleDIP(4);
        if(keyonly(6)) FCEUI_VSUniToggleDIP(5);
        if(keyonly(7)) FCEUI_VSUniToggleDIP(6);
        if(keyonly(8)) FCEUI_VSUniToggleDIP(7);
  }
  else
  {
   static uint8 bbuf[32];
   static int bbuft;
   static int barcoder = 0;

   if(keyonly(H)) FCEUI_NTSCSELHUE();
   if(keyonly(T)) FCEUI_NTSCSELTINT();
   if(KEY(KP_MINUS) || KEY(MINUS)) FCEUI_NTSCDEC();
   if(KEY(KP_PLUS) || KEY(EQUAL)) FCEUI_NTSCINC();

   if((InputType[2] == SIFC_BWORLD) || (cspec == SIS_DATACH))
   {
    if(keyonly(F8))
    {
     barcoder ^= 1;
     if(!barcoder)
     {
      if(InputType[2] == SIFC_BWORLD)
      {
       strcpy(&BWorldData[1],bbuf);
       BWorldData[0]=1;
      }
      else
       FCEUI_DatachSet(bbuf);
      FCEUI_DispMessage("Barcode Entered");
     }
     else { bbuft = 0; FCEUI_DispMessage("Enter Barcode");}
    }
   } else barcoder = 0;

        #define SSM(x)                \
        { if(barcoder) { if(bbuft < 13) {bbuf[bbuft++] = '0' + x; bbuf[bbuft] = 0;} FCEUI_DispMessage("Barcode: %s",bbuf);}        \
        else {                         \
         if(is_shift) FCEUI_SelectMovie(x);         \
         else FCEUI_SelectState(x);         \
        } }

   DIPSless:
   if(keyonly(0)) SSM(0);
   if(keyonly(1)) SSM(1);
   if(keyonly(2)) SSM(2);
   if(keyonly(3)) SSM(3);
   if(keyonly(4)) SSM(4);
   if(keyonly(5)) SSM(5);
   if(keyonly(6)) SSM(6);
   if(keyonly(7)) SSM(7);
   if(keyonly(8)) SSM(8);
   if(keyonly(9)) SSM(9);
   #undef SSM
 }
}

#define MK(x)   {{BUTTC_KEYBOARD},{0},{MKK(x)},1}
#define MC(x)   {{BUTTC_KEYBOARD},{0},{x},1}
#define MK2(x1,x2)        {{BUTTC_KEYBOARD},{0},{MKK(x1),MKK(x2)},2}

#define MKZ()   {{0},{0},{0},0}

#define GPZ()   {MKZ(), MKZ(), MKZ(), MKZ()}

ButtConfig GamePadConfig[4][10]={
        /* Gamepad 1 */
        {
         MK(LEFTALT), MK(LEFTCONTROL), MK(TAB), MK(ENTER), MK(BL_CURSORUP),
          MK(BL_CURSORDOWN),MK(BL_CURSORLEFT),MK(BL_CURSORRIGHT)
        },

        /* Gamepad 2 */
        GPZ(),

        /* Gamepad 3 */
        GPZ(),

        /* Gamepad 4 */
        GPZ()
};


static void UpdateGamepad(void)
{
 static int rapid=0;
 uint32 JS=0;
 int x;
 int wg;

 rapid^=1;

 for(wg=0;wg<4;wg++)
 {
  for(x=0;x<8;x++)
   if(DTestButton(&GamePadConfig[wg][x]))
    JS|=(1<<x)<<(wg<<3);

  if(rapid)
   for(x=0;x<2;x++)
     if(DTestButton(&GamePadConfig[wg][8+x]))
      JS|=(1<<x)<<(wg<<3);
  }

  for(x=0;x<32;x+=8)        /* Now, test to see if anything weird(up+down at same time)
                           is happening, and correct */
  {
   if((JS & (0xC0<<x) ) == (0xC0<<x) ) JS&=~(0xC0<<x);
   if((JS & (0x30<<x) ) == (0x30<<x) ) JS&=~(0x30<<x);
  }

  JSreturn=JS;
}

ButtConfig powerpadsc[2][12]={
                              {
                               MK(O),MK(P),MK(BRACKET_LEFT),
                               MK(BRACKET_RIGHT),

                               MK(K),MK(L),MK(SEMICOLON),
                                MK(APOSTROPHE),
                               MK(M),MK(COMMA),MK(PERIOD),MK(SLASH)
                              },
                              {
                               MK(O),MK(P),MK(BRACKET_LEFT),
                               MK(BRACKET_RIGHT),MK(K),MK(L),MK(SEMICOLON),
                                MK(APOSTROPHE),
                               MK(M),MK(COMMA),MK(PERIOD),MK(SLASH)
                              }
                             };

static uint32 powerpadbuf[2];

static uint32 UpdatePPadData(int w)
{
 uint32 r=0;
 ButtConfig *ppadtsc=powerpadsc[w];
 int x;

 for(x=0;x<12;x++)
  if(DTestButton(&ppadtsc[x])) r|=1<<x;

 return r;
}


static uint8 fkbkeys[0x48];
static uint8 suborkbkeys[0x60];

void FCEUD_UpdateInput(void)
{
  int x;
  int t=0;

  KeyboardUpdateState();
  UpdateJoysticks();

  //UpdatePhysicalInput();
  KeyboardCommands();

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

  switch(InputType[2])
  {
   case SIFC_ARKANOID:t|=2;break;
   case SIFC_SHADOW:t|=2;break;
   case SIFC_FKB:if(cidisabled) UpdateFKB();break;
   case SIFC_SUBORKB:if(cidisabled) UpdateSuborKB();break;
   case SIFC_HYPERSHOT: UpdateHyperShot();break;
   case SIFC_MAHJONG: UpdateMahjong();break;
   case SIFC_QUIZKING: UpdateQuizKing();break;
   case SIFC_FTRAINERB:
   case SIFC_FTRAINERA: UpdateFTrainer();break;
   case SIFC_TOPRIDER: UpdateTopRider();break;
   case SIFC_OEKAKIDS:t|=2;break;

  }

  if(t&1)
   UpdateGamepad();

  if(t&2)
   GetMouseData(MouseData);
}

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_POWERPADA:
     case SI_POWERPADB:InputDPtr=&powerpadbuf[x];break;
     case SI_GAMEPAD:InputDPtr=&JSreturn;break;
     case SI_ARKANOID:InputDPtr=MouseData;t|=1;break;
     case SI_ZAPPER:InputDPtr=MouseData;
                                t|=1;
                                attrib=screenmode;
                                break;
    }
    FCEUI_SetInput(x,InputType[x],InputDPtr,attrib);
   }

   attrib=0;
   InputDPtr=0;
   switch(InputType[2])
   {
    case SIFC_SHADOW:InputDPtr=MouseData;t|=1;attrib=screenmode;break;
    case SIFC_OEKAKIDS:InputDPtr=MouseData;t|=1;attrib=screenmode;break;
    case SIFC_ARKANOID:InputDPtr=MouseData;t|=1;break;
    case SIFC_FKB:InputDPtr=fkbkeys;break;
    case SIFC_SUBORKB:InputDPtr=suborkbkeys;break;
    case SIFC_HYPERSHOT:InputDPtr=&HyperShotData;break;
    case SIFC_MAHJONG:InputDPtr=&MahjongData;break;
    case SIFC_QUIZKING:InputDPtr=&QuizKingData;break;
    case SIFC_TOPRIDER:InputDPtr=&TopRiderData;break;
    case SIFC_BWORLD:InputDPtr=BWorldData;break;
    case SIFC_FTRAINERA:
    case SIFC_FTRAINERB:InputDPtr=&FTrainerData;break;
   }

   FCEUI_SetInputFC(InputType[2],InputDPtr,attrib);
   FCEUI_DisableFourScore(eoptions&EO_NOFOURSCORE);
}

ButtConfig fkbmap[0x48]=
{
 MK(F1),MK(F2),MK(F3),MK(F4),MK(F5),MK(F6),MK(F7),MK(F8),
 MK(1),MK(2),MK(3),MK(4),MK(5),MK(6),MK(7),MK(8),MK(9),MK(0),
        MK(MINUS),MK(EQUAL),MK(BACKSLASH),MK(BACKSPACE),
 MK(ESCAPE),MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I),MK(O),
        MK(P),MK(GRAVE),MK(BRACKET_LEFT),MK(ENTER),
 MK(LEFTCONTROL),MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K),
        MK(L),MK(SEMICOLON),MK(APOSTROPHE),MK(BRACKET_RIGHT),MK(INSERT),
 MK(LEFTSHIFT),MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M),MK(COMMA),
        MK(PERIOD),MK(SLASH),MK(RIGHTALT),MK(RIGHTSHIFT),MK(LEFTALT),MK(SPACE),
 MK(BL_DELETE),
 MK(BL_END),
 MK(BL_PAGEDOWN),
 MK(BL_CURSORUP),MK(BL_CURSORLEFT),MK(BL_CURSORRIGHT),MK(BL_CURSORDOWN)
};

ButtConfig suborkbmap[0x60]=
{
 MC(0x01),MC(0x3b),MC(0x3c),MC(0x3d),MC(0x3e),MC(0x3f),MC(0x40),MC(0x41),MC(0x42),MC(0x43),
 MC(0x44),MC(0x57),MC(0x58),MC(0x45),MC(0x29),MC(0x02),MC(0x03),MC(0x04),MC(0x05),MC(0x06),
 MC(0x07),MC(0x08),MC(0x09),MC(0x0a),MC(0x0b),MC(0x0c),MC(0x0d),MC(0x0e),MC(0xd2),MC(0xc7),
 MC(0xc9),MC(0xc5),MC(0xb5),MC(0x37),MC(0x4a),MC(0x0f),MC(0x10),MC(0x11),MC(0x12),MC(0x13),
 MC(0x14),MC(0x15),MC(0x16),MC(0x17),MC(0x18),MC(0x19),MC(0x1a),MC(0x1b),MC(0x1c),MC(0xd3),
 MC(0xca),MC(0xd1),MC(0x47),MC(0x48),MC(0x49),MC(0x4e),MC(0x3a),MC(0x1e),MC(0x1f),MC(0x20),
 MC(0x21),MC(0x22),MC(0x23),MC(0x24),MC(0x25),MC(0x26),MC(0x27),MC(0x28),MC(0x4b),MC(0x4c),
 MC(0x4d),MC(0x2a),MC(0x2c),MC(0x2d),MC(0x2e),MC(0x2f),MC(0x30),MC(0x31),MC(0x32),MC(0x33),
 MC(0x34),MC(0x35),MC(0x2b),MC(0xc8),MC(0x4f),MC(0x50),MC(0x51),MC(0x1d),MC(0x38),MC(0x39),
 MC(0xcb),MC(0xd0),MC(0xcd),MC(0x52),MC(0x53)
};


static void UpdateFKB(void)
{
 int x;

 for(x=0;x<0x48;x++)
 {
  fkbkeys[x]=0;

  if(DTestButton(&fkbmap[x]))
   fkbkeys[x]=1;
 }
}

static void UpdateSuborKB(void)
{
 int x;

 for(x=0;x<0x60;x++)
 {
  suborkbkeys[x]=0;

  if(DTestButton(&suborkbmap[x]))
   suborkbkeys[x]=1;
 }
}

static ButtConfig HyperShotButtons[4]=
{
 MK(Q),MK(W),MK(E),MK(R)
};

static void UpdateHyperShot(void)
{
 int x;

 HyperShotData=0;
 for(x=0;x<0x4;x++)
 {
  if(DTestButton(&HyperShotButtons[x]))
   HyperShotData|=1<<x;
 }
}

static ButtConfig MahjongButtons[21]=
{
 MK(Q),MK(W),MK(E),MK(R),MK(T),
 MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K),MK(L),
 MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M)
};

static void UpdateMahjong(void)
{
 int x;

 MahjongData=0;
 for(x=0;x<21;x++)
 {
  if(DTestButton(&MahjongButtons[x]))
   MahjongData|=1<<x;
 }
}

ButtConfig QuizKingButtons[6]=
{
 MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y)
};

static void UpdateQuizKing(void)
{
 int x;

 QuizKingData=0;

 for(x=0;x<6;x++)
 {
  if(DTestButton(&QuizKingButtons[x]))
   QuizKingData|=1<<x;
 }

}

ButtConfig TopRiderButtons[8]=
{
 MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I)
};

static void UpdateTopRider(void)
{
 int x;
 TopRiderData=0;
 for(x=0;x<8;x++)
  if(DTestButton(&TopRiderButtons[x]))
   TopRiderData|=1<<x;
}

ButtConfig FTrainerButtons[12]=
{
                               MK(O),MK(P),MK(BRACKET_LEFT),
                               MK(BRACKET_RIGHT),MK(K),MK(L),MK(SEMICOLON),
                                MK(APOSTROPHE),
                               MK(M),MK(COMMA),MK(PERIOD),MK(SLASH)
};

static void UpdateFTrainer(void)
{
 int x;

 FTrainerData=0;

 for(x=0;x<12;x++)
 {
  if(DTestButton(&FTrainerButtons[x]))
   FTrainerData|=1<<x;
 }
}

int DWaitButton(HWND hParent, const uint8 *text, ButtConfig *bc);

CFGSTRUCT InputConfig[]={
        ACA(UsrInputType),
        AC(powerpadsc),
        AC(QuizKingButtons),
        AC(FTrainerButtons),
        AC(HyperShotButtons),
        AC(MahjongButtons),
        AC(GamePadConfig),
        AC(fkbmap),
        AC(suborkbmap),
        ENDCFGSTRUCT
};

void InitInputStuff(void)
{
   int x,y;

   KeyboardInitialize();
   InitJoysticks(hAppWnd);

   for(x=0; x<4; x++)
    for(y=0; y<10; y++)
     JoyClearBC(&GamePadConfig[x][y]);

   for(x=0; x<2; x++)
    for(y=0; y<12; y++)
     JoyClearBC(&powerpadsc[x][y]);

   for(x=0; x<0x48; x++)
    JoyClearBC(&fkbmap[x]);
   for(x=0; x<0x60; x++)
    JoyClearBC(&suborkbmap[x]);

   for(x=0; x<6; x++)
    JoyClearBC(&QuizKingButtons[x]);
   for(x=0; x<12; x++)
    JoyClearBC(&FTrainerButtons[x]);
   for(x=0; x<21; x++)
    JoyClearBC(&MahjongButtons[x]);
   for(x=0; x<4; x++)
    JoyClearBC(&HyperShotButtons[x]);
}



static void FCExp(char *text)
{
        static char *fccortab[12]={"none","arkanoid","shadow","4player","fkb","suborkb",
                    "hypershot","mahjong","quizking","ftrainera","ftrainerb","oekakids"};

        static int fccortabi[12]={SIFC_NONE,SIFC_ARKANOID,SIFC_SHADOW,
                                 SIFC_4PLAYER,SIFC_FKB,SIFC_SUBORKB,SIFC_HYPERSHOT,SIFC_MAHJONG,SIFC_QUIZKING,
                                 SIFC_FTRAINERA,SIFC_FTRAINERB,SIFC_OEKAKIDS};
        int y;
        for(y=0;y<12;y++)
         if(!strcmp(fccortab[y],text))
          UsrInputType[2]=fccortabi[y];
}

static char *cortab[6]={"none","gamepad","zapper","powerpada","powerpadb","arkanoid"};
static int cortabi[6]={SI_NONE,SI_GAMEPAD,
                               SI_ZAPPER,SI_POWERPADA,SI_POWERPADB,SI_ARKANOID};

static void Input1(char *text)
{
        int y;

        for(y=0;y<6;y++)
         if(!strcmp(cortab[y],text))
          UsrInputType[0]=cortabi[y];
}

static void Input2(char *text)
{
        int y;

        for(y=0;y<6;y++)
         if(!strcmp(cortab[y],text))
          UsrInputType[1]=cortabi[y];
}

ARGPSTRUCT InputArgs[]={
        {"-fcexp",0,(void *)FCExp,0x2000},
        {"-input1",0,(void *)Input1,0x2000},
        {"-input2",0,(void *)Input2,0x2000},
        {0,0,0,0}
};

int DTestButton(ButtConfig *bc)
{
 int x;

 for(x=0;x<bc->NumC;x++)
 {
  if(bc->ButtType[x]==BUTTC_KEYBOARD)
  {
   if(keys[bc->ButtonNum[x]])
   {
    return(1);
   }
  }
 }
 if(DTestButtonJoy(bc)) return(1);
 return(0);
}

static char *MakeButtString(ButtConfig *bc)
{
 int x;
 char tmpstr[512];
 char *astr;

 tmpstr[0] = 0;

 for(x=0;x<bc->NumC;x++)
 {
  if(x) strcat(tmpstr, ", ");

  if(bc->ButtType[x] == BUTTC_KEYBOARD)
  {
   strcat(tmpstr,"KB: ");
   if(!GetKeyNameText(bc->ButtonNum[x]<<16,tmpstr+strlen(tmpstr),16))
    sprintf(tmpstr+strlen(tmpstr),"%02X",bc->ButtonNum[x]);
   else
    sprintf(tmpstr+strlen(tmpstr)," (%02X)",bc->ButtonNum[x]);
  }
  else if(bc->ButtType[x] == BUTTC_JOYSTICK)
  {
   strcat(tmpstr,"JS ");
   sprintf(tmpstr+strlen(tmpstr), "%d ", bc->DeviceNum[x]);
   if(bc->ButtonNum[x] & 0x8000)
   {
    char *asel[3]={"x","y","z"};
    sprintf(tmpstr+strlen(tmpstr), "axis %s%s", asel[bc->ButtonNum[x] & 3],(bc->ButtonNum[x]&0x4000)?"-":"+");
   }
   else if(bc->ButtonNum[x] & 0x2000)
   {
    sprintf(tmpstr+strlen(tmpstr), "hat %d:%d", (bc->ButtonNum[x] >> 4)&3,
        bc->ButtonNum[x]&3);
   }
   else
   {
    sprintf(tmpstr+strlen(tmpstr), "button %d", bc->ButtonNum[x] & 127);
   }

  }
 }

 astr=malloc(strlen(tmpstr) + 1);
 strcpy(astr,tmpstr);
 return(astr);
}


static int DWBStarted;
static ButtConfig *DWBButtons;
static const uint8 *DWBText;

static HWND die;

static BOOL CALLBACK DWBCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

  switch(uMsg) {
   case WM_DESTROY:
                   die = NULL;
                   return(0);
   case WM_TIMER:
                {
                 uint8 devicenum;
                 uint16 buttonnum;
                 GUID guid;

                 if(DoJoyWaitTest(&guid, &devicenum, &buttonnum))
                 {
                  ButtConfig *bc = DWBButtons;
                  char *nstr;
                  int wc;
                  if(DWBStarted)
                  {
                       ButtConfig *bc = DWBButtons;
                       bc->NumC = 0;
                       DWBStarted = 0;
                  }
                  wc = bc->NumC;
                  //FCEU_printf("%d: %d\n",devicenum,buttonnum);
                  bc->ButtType[wc]=BUTTC_JOYSTICK;
                  bc->DeviceNum[wc]=devicenum;
                  bc->ButtonNum[wc]=buttonnum;
                  bc->DeviceInstance[wc] = guid;

                  /* Stop config if the user pushes the same button twice in a row. */
                  if(wc && bc->ButtType[wc]==bc->ButtType[wc-1] && bc->DeviceNum[wc]==bc->DeviceNum[wc-1] &&
                   bc->ButtonNum[wc]==bc->ButtonNum[wc-1])
                    goto gornk;

                   bc->NumC++;

                   /* Stop config if we reached our maximum button limit. */
                   if(bc->NumC >= MAXBUTTCONFIG)
                   goto gornk;
                   nstr = MakeButtString(bc);
                   SetDlgItemText(hwndDlg, 100, nstr);
                   free(nstr);
                 }
                }
                break;
   case WM_USER + 666:
                      //SetFocus(GetDlgItem(hwndDlg,100));
                      if(DWBStarted)
                      {
                       char *nstr;
                       ButtConfig *bc = DWBButtons;
                       bc->NumC = 0;
                       DWBStarted = 0;
                       nstr = MakeButtString(bc);
                       SetDlgItemText(hwndDlg, 100, nstr);
                       free(nstr);
                      }

                      {
                       ButtConfig *bc = DWBButtons;
                       int wc = bc->NumC;
                       char *nstr;

                       bc->ButtType[wc]=BUTTC_KEYBOARD;
                       bc->DeviceNum[wc]=0;
                       bc->ButtonNum[wc]=lParam&255;

                       /* Stop config if the user pushes the same button twice in a row. */
                       if(wc && bc->ButtType[wc]==bc->ButtType[wc-1] && bc->DeviceNum[wc]==bc->DeviceNum[wc-1] &&
                        bc->ButtonNum[wc]==bc->ButtonNum[wc-1])
                        goto gornk;

                       bc->NumC++;
                       /* Stop config if we reached our maximum button limit. */
                       if(bc->NumC >= MAXBUTTCONFIG)
                        goto gornk;

                       nstr = MakeButtString(bc);
                       SetDlgItemText(hwndDlg, 100, nstr);
                       free(nstr);
                      }
                      break;
   case WM_INITDIALOG:
                      SetWindowText(hwndDlg, DWBText);
                      BeginJoyWait(hwndDlg);
                      SetTimer(hwndDlg,666,25,0);     /* Every 25ms.*/
                      {
                       char *nstr = MakeButtString(DWBButtons);
                       SetDlgItemText(hwndDlg, 100, nstr);
                       free(nstr);
                      }
                      break;
   case WM_CLOSE:
   case WM_QUIT: goto gornk;

   case WM_COMMAND:
                 switch(wParam&0xFFFF)
                 {
                  case 200:
                           {
                            ButtConfig *bc = DWBButtons;
                            char *nstr;
                            bc->NumC = 0;
                            nstr = MakeButtString(bc);
                            SetDlgItemText(hwndDlg, 100, nstr);
                            free(nstr);
                           }
                           break;
                  case 201:
                         gornk:
                         KillTimer(hwndDlg,666);
                         EndJoyWait(hAppWnd);
                         SetForegroundWindow(GetParent(hwndDlg));
                         DestroyWindow(hwndDlg);
                         break;
                 }
              }
  return 0;
}

int DWaitButton(HWND hParent, const uint8 *text, ButtConfig *bc)
{
 DWBText=text;
 DWBButtons = bc;
 DWBStarted = 1;

 die = CreateDialog(fceu_hInstance, "DWBDIALOG", hParent, DWBCallB);

 EnableWindow(hParent, 0);

 ShowWindow(die, 1);

 while(die)
 {
  MSG msg;
  while(PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
  {
   if(GetMessage(&msg, 0, 0, 0) > 0)
   {
    if(msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
    {
     LPARAM tmpo;

     tmpo=((msg.lParam>>16)&0x7F)|((msg.lParam>>17)&0x80);
     PostMessage(die,WM_USER+666,0,tmpo);
     continue;
    }
    if(msg.message == WM_SYSCOMMAND) continue;
    if(!IsDialogMessage(die, &msg))
    {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
    }
   }
  }
  Sleep(10);
 }

 EnableWindow(hParent, 1);
}

static ButtConfig *DoTBButtons;
static const char *DoTBTitle;
static int DoTBMax;
static int DoTBType,DoTBPort;

static BOOL CALLBACK DoTBCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch(uMsg) {
   case WM_INITDIALOG:
                      if(DoTBType == SI_GAMEPAD)
                      {
                       char buf[32];
                       sprintf(buf,"Virtual Gamepad %d",DoTBPort+1);
                       SetDlgItemText(hwndDlg, 100,buf);

                       sprintf(buf,"Virtual Gamepad %d",DoTBPort+3);
                       SetDlgItemText(hwndDlg, 101, buf);

                       CheckDlgButton(hwndDlg,400,(eoptions & EO_NOFOURSCORE)?BST_CHECKED:BST_UNCHECKED);
                      }
                      SetWindowText(hwndDlg, DoTBTitle);
                      break;
   case WM_CLOSE:
   case WM_QUIT: goto gornk;

   case WM_COMMAND:
                {
                 int b;
                 b=wParam&0xFFFF;
                 if(b>= 300 && b < (300 + DoTBMax))
                 {
                  char btext[128];
                  btext[0]=0;
                  GetDlgItemText(hwndDlg, b, btext, 128);
                  DWaitButton(hwndDlg, btext,&DoTBButtons[b - 300]);
                 }
                 else switch(wParam&0xFFFF)
                 {
                  case 1:
                         gornk:

                         if(DoTBType == SI_GAMEPAD)
                         {
                          eoptions &= ~EO_NOFOURSCORE;
                          if(IsDlgButtonChecked(hwndDlg,400)==BST_CHECKED)
                           eoptions|=EO_NOFOURSCORE;
                         }
                         EndDialog(hwndDlg,0);
                         break;
                 }
                }
              }
  return 0;

}

static void DoTBConfig(HWND hParent, const char *text, char *template, ButtConfig *buttons, int max)
{
  DoTBTitle=text;
  DoTBButtons = buttons;
  DoTBMax = max;
  DialogBox(fceu_hInstance,template,hParent,DoTBCallB);
}

static BOOL CALLBACK InputConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  static const char *strn[6]={"<none>","Gamepad","Zapper","Power Pad A","Power Pad B","Arkanoid Paddle"};
  static const char *strf[14]=
  {"<none>","Arkanoid Paddle","Hyper Shot gun","4-Player Adapter",
  "Family Keyboard","Subor Keyboard","HyperShot Pads", "Mahjong", "Quiz King Buzzers",
  "Family Trainer A","Family Trainer B", "Oeka Kids Tablet", "Barcode World",
  "Top Rider"};
  static const int haven[6]={0,1,0,1,1,0};
  static const int havef[14]={0,0,0,0, 1,1,0,0, 1,1,1,0, 0,0};
  int x;

  switch(uMsg) {
   case WM_INITDIALOG:
                for(x=0;x<2;x++)
                {
                 int y;

                 for(y=0;y<6;y++)
                  SendDlgItemMessage(hwndDlg,104+x,CB_ADDSTRING,0,(LPARAM)(LPSTR)strn[y]);

                 SendDlgItemMessage(hwndDlg,104+x,CB_SETCURSEL,UsrInputType[x],(LPARAM)(LPSTR)0);
                 EnableWindow(GetDlgItem(hwndDlg,106+x),haven[InputType[x]]);
                 SetDlgItemText(hwndDlg,200+x,(LPTSTR)strn[InputType[x]]);
                }


                {
                 int y;
                 for(y=0;y<13;y++)
                  SendDlgItemMessage(hwndDlg,110,CB_ADDSTRING,0,(LPARAM)(LPSTR)strf[y]);
                 SendDlgItemMessage(hwndDlg,110,CB_SETCURSEL,UsrInputType[2],(LPARAM)(LPSTR)0);
                 EnableWindow(GetDlgItem(hwndDlg,111),havef[InputType[2]]);
                 SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputType[2]]);
                }

                break;
   case WM_CLOSE:
   case WM_QUIT: goto gornk;
   case WM_COMMAND:
                if(HIWORD(wParam)==CBN_SELENDOK)
                {
                 switch(LOWORD(wParam))
                 {
                  case 104:
                  case 105:UsrInputType[LOWORD(wParam)-104]=InputType[LOWORD(wParam)-104]=SendDlgItemMessage(hwndDlg,LOWORD(wParam),CB_GETCURSEL,0,(LPARAM)(LPSTR)0);
                           EnableWindow( GetDlgItem(hwndDlg,LOWORD(wParam)+2),haven[InputType[LOWORD(wParam)-104]]);
                           SetDlgItemText(hwndDlg,200+LOWORD(wParam)-104,(LPTSTR)strn[InputType[LOWORD(wParam)-104]]);
                           break;
                  case 110:UsrInputType[2]=InputType[2]=SendDlgItemMessage(hwndDlg,110,CB_GETCURSEL,0,(LPARAM)(LPSTR)0);
                           EnableWindow(GetDlgItem(hwndDlg,111),havef[InputType[2]]);
                           SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputType[2]]);
                           break;

                 }

                }
                if(!(wParam>>16))
                switch(wParam&0xFFFF)
                {
                 case 111:
                 {
                  const char *text = strf[InputType[2]];
                  DoTBType=DoTBPort=0;

                  switch(InputType[2])
                  {
                   case SIFC_FTRAINERA:
                   case SIFC_FTRAINERB:DoTBConfig(hwndDlg, text, "POWERPADDIALOG", FTrainerButtons, 12); break;
                   case SIFC_FKB:DoTBConfig(hwndDlg, text, "FKBDIALOG",fkbmap,0x48);break;
                   case SIFC_SUBORKB:DoTBConfig(hwndDlg, text, "SUBORKBDIALOG",suborkbmap,0x60);break;
                   case SIFC_QUIZKING:DoTBConfig(hwndDlg, text, "QUIZKINGDIALOG",QuizKingButtons,6);break;
                  }
                 }
                 break;

                 case 107:
                 case 106:
                  {
                   int which=(wParam&0xFFFF)-106;
                   const char *text = strn[InputType[which]];

                   DoTBType=DoTBPort=0;
                   switch(InputType[which])
                   {
                    case SI_GAMEPAD:
                    {
                     ButtConfig tmp[10 + 10];

                     memcpy(tmp, GamePadConfig[which], 10 * sizeof(ButtConfig));
                     memcpy(&tmp[10], GamePadConfig[which+2], 10 * sizeof(ButtConfig));

                     DoTBType=SI_GAMEPAD;
                     DoTBPort=which;
                     DoTBConfig(hwndDlg, text, "GAMEPADDIALOG", tmp, 10 + 10);

                     memcpy(GamePadConfig[which], tmp, 10 * sizeof(ButtConfig));
                     memcpy(GamePadConfig[which+2], &tmp[10], 10 * sizeof(ButtConfig));
                    }
                    break;

                    case SI_POWERPADA:
                    case SI_POWERPADB:
                     DoTBConfig(hwndDlg, text, "POWERPADDIALOG",powerpadsc[which],12);
                     break;
                   }
                  }
                  break;

                 case 1:
                        gornk:
                        EndDialog(hwndDlg,0);
                        break;
               }
              }
  return 0;
}

void ConfigInput(HWND hParent)
{
  DialogBox(fceu_hInstance,"INPUTCONFIG",hParent,InputConCallB);
  if(GI)
   InitOtherInput();
}



void DestroyInput(void)
{
 if(lpDI)
 {
  KillJoysticks();
  KeyboardClose();
  IDirectInput7_Release(lpDI);
 }
}
