#include "common.h"
#include "../../x6502.h"
#include "../../debug.h"
#include "debug.h"

static void DoMemmo(HWND hParent);
void UpdateDebugger(void);
static int discallb(uint16 a, char *s);
static void crs(HWND hParent);
static void Disyou(uint16 a);
static void UpdateDMem(uint16 a);

static HWND mwin=0;
static int32 cmsi;
static int instep=-1;
static X6502 *Xsave;

HWND dwin;

static uint16 StrToU16(char *s)
{
 unsigned int ret=0;
 sscanf(s,"%4x",&ret);
 return ret;
}

static uint8 StrToU8(char *s)
{
 unsigned int ret=0;
 sscanf(s,"%2x",&ret);
 return ret;
}

/* Need to be careful where these functions are used. */
static char *U16ToStr(uint16 a)
{
 static char TempArray[16];
 sprintf(TempArray,"%04X",a);
 return TempArray;
}

static char *U8ToStr(uint8 a)
{
 static char TempArray[16];
 sprintf(TempArray,"%02X",a);
 return TempArray;
}

static uint16 asavers[27];

static int discount;
static int discallb(uint16 a, char *s)
{
 SendDlgItemMessage(dwin,100,LB_ADDSTRING,0,(LPARAM)(LPSTR)s);
 if(a==Xsave->PC)
  SendDlgItemMessage(dwin,100,LB_SETCURSEL,discount,0);
 asavers[discount]=a;
 discount++;
 return((discount>25)?0:1);
}

static void crs(HWND hParent)
{
// SetDlgItemText(hParent,320,(LPTSTR)(instep>=0)?"Stopped.":"Running...");
 EnableWindow( GetDlgItem(hParent,301),(instep>=0)?1:0);
}

static void Disyou(uint16 a)
{
 discount=0;
 SendDlgItemMessage(dwin,100,LB_RESETCONTENT,0,0);
 FCEUI_Disassemble(Xsave,a,discallb);
}

static void SSI(X6502 *X)
{
 SCROLLINFO si;

 memset(&si,0,sizeof(si));
 si.cbSize=sizeof(si);
 si.fMask=SIF_ALL;
 si.nMin=-32768;
 si.nMax=32767;
 si.nPage=27;
 si.nPos=X->PC-32768;
 SetScrollInfo(GetDlgItem(dwin,101),SB_CTL,&si,1);
}

static void RFlags(X6502 *X)
{
 int x;

 SetDlgItemText(dwin,202,(LPTSTR)U8ToStr(X->P&~0x30));
 for(x=0;x<8;x++)
 {
  if(x!=4 && x!=5)
   CheckDlgButton(dwin,400+x,((X->P>>x)&1)?BST_CHECKED:BST_UNCHECKED);
 }
}

static void RRegs(X6502 *X)
{
 SetDlgItemText(dwin,200,(LPTSTR)U16ToStr(X->PC));
 SetDlgItemText(dwin,201,(LPTSTR)U8ToStr(X->S));
 SetDlgItemText(dwin,210,(LPTSTR)U8ToStr(X->A));
 SetDlgItemText(dwin,211,(LPTSTR)U8ToStr(X->X));
 SetDlgItemText(dwin,212,(LPTSTR)U8ToStr(X->Y));
}

static void DoVecties(HWND hParent)
{
  uint16 m[3];
  int x;

  FCEUI_GetIVectors(&m[2],&m[0],&m[1]);
  for(x=0;x<3;x++)
   SetDlgItemText(hParent,220+x,(LPTSTR)U16ToStr(m[x]));
}

static void Redrawsy(X6502 *X)
{
 SSI(X);
 RFlags(X);
 RRegs(X);

 Disyou(X->PC);

 DoVecties(dwin);
}

static void MultiCB(X6502 *X)
{
 Xsave=X;
 if(instep>=0)
 {  
  Redrawsy(X);
  if(mwin)
   UpdateDMem(cmsi);
 }

 while(instep>=0)
 {
  if(instep>=1)
  {
   instep--;
   return;
  }
  StopSound();
  if(!BlockingCheck())		/* Whoops, need to exit for some reason. */
  {
   instep=-1;
   FCEUI_SetCPUCallback(0);
   return;
  }
  Sleep(50);
 }
}

static int multistep=0;
static void cpucb(X6502 *X)
{
 multistep=0;
 MultiCB(X);
}

static void BPointHandler(X6502 *X, int type, unsigned int A)
{
 if(multistep) return;  /* To handle instructions that can cause multiple
                           breakpoints per instruction.
                        */
 if(!dwin) return;      /* Window is closed, don't do breakpoints. */
 multistep=1;
 instep=0;
 crs(dwin);
 MultiCB(X);
}

static HWND hWndCallB;
static int RBPCallBack(int type, unsigned int A1, unsigned int A2,
        void (*Handler)(X6502 *, int type, unsigned int A) )
{
 char buf[128];
 sprintf(buf,"%s%s%s $%04x - $%04x",(type&BPOINT_READ)?"R":" ",
        (type&BPOINT_WRITE)?"W":" ",(type&BPOINT_PC)?"P":" ",A1,A2);
 SendDlgItemMessage(hWndCallB,510,LB_ADDSTRING,0,(LPARAM)(LPSTR)buf);
 return(1);
}

static void RebuildBPointList(HWND hwndDlg)
{
 SendDlgItemMessage(hwndDlg,510,LB_RESETCONTENT,0,0);
 hWndCallB=hwndDlg;
 FCEUI_ListBreakPoints(RBPCallBack);
}

static void FetchBPDef(HWND hwndDlg, uint16 *A1, uint16 *A2, int *type)
{
 char tmp[256];

 GetDlgItemText(hwndDlg,520,tmp,256);
 *A1=StrToU16(tmp);
 GetDlgItemText(hwndDlg,521,tmp,256);
 if(tmp[0]==0)
  *A2=*A1;
 else
  *A2=StrToU16(tmp);

 *type=0;
 *type|=(IsDlgButtonChecked(hwndDlg,530)==BST_CHECKED)?BPOINT_READ:0;
 *type|=(IsDlgButtonChecked(hwndDlg,531)==BST_CHECKED)?BPOINT_WRITE:0;
 *type|=(IsDlgButtonChecked(hwndDlg,532)==BST_CHECKED)?BPOINT_PC:0;
}

static void SetBPDef(HWND hwndDlg, int32 A1, int32 A2, int type)
{
 if(A1>=0)
  SetDlgItemText(hwndDlg,520,(LPTSTR)U16ToStr(A1));
 if(A2>=0)
  SetDlgItemText(hwndDlg,521,(LPTSTR)U16ToStr(A2));
 if(type>=0)
 {
 // CheckDlgButton(hwndDlg,123,(soundoptions&SO_SECONDARY)?BST_CHECKED:BST_UNCHECKED);  
 }
}

static BOOL CALLBACK DebugCon(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  int32 scrollindex=0,ced=0;
  DSMFix(uMsg);
  switch(uMsg)
  {
   case WM_INITDIALOG:
                crs(hwndDlg);
                DoVecties(hwndDlg);
                FCEUI_SetCPUCallback(cpucb);
                RebuildBPointList(hwndDlg);
                break;
   case WM_VSCROLL:
                if(0!=instep) break;
                switch((int)LOWORD(wParam))
                {
                  case SB_TOP:
                        scrollindex=-32768;
                        ced=1;
                        break;
                  case SB_BOTTOM:
                        scrollindex=32767;
                        ced=1;
                        break;
                  case SB_LINEUP:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,101),SB_CTL);
                        if(scrollindex>-32768)
                        {
                         scrollindex--;
                         ced=1;
                        }
                        break;

                  case SB_PAGEUP:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,101),SB_CTL);
                        scrollindex-=27;
                        if(scrollindex<-32768)
                         scrollindex=-32768;
                        ced=1;
                        break;

                  case SB_LINEDOWN:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,101),SB_CTL);
                        if(scrollindex<32767)
                        {
                         scrollindex++;
                         ced=1;
                        }
                        break;

                  case SB_PAGEDOWN:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,101),SB_CTL);
                        scrollindex+=27;
                        if(scrollindex>32767)
                        {
                         scrollindex=32767;
                        }
                        ced=1;
                        break;

                  case SB_THUMBPOSITION:
                  case SB_THUMBTRACK:
                        scrollindex=(int16)(wParam>>16);
                        ced=1;
                        break;
                 }
                 if(ced)
                 {
                  SendDlgItemMessage(hwndDlg,101,SBM_SETPOS,scrollindex,1);
                  Disyou(scrollindex+32768);
                 }
                 break;


   case WM_CLOSE:
   case WM_QUIT: goto gornk;
   case WM_COMMAND:
                switch(HIWORD(wParam))
                {
                 case LBN_DBLCLK:
                         if(0==instep)
                         {
                          Xsave->PC=asavers[SendDlgItemMessage(hwndDlg,100,LB_GETCURSEL,0,0)];
                          RRegs(Xsave);
                          Disyou(Xsave->PC);
                         }
                         break;
                 case EN_KILLFOCUS:
                        {
                         char TempArray[64];
                         int id=LOWORD(wParam);
                         GetDlgItemText(hwndDlg,id,TempArray,64);

                         if(0==instep)
                         {
                          int tscroll=32768+SendDlgItemMessage(hwndDlg,101,SBM_GETPOS,0,0);

                          switch(id)
                          {
                           case 200:                                    
                                    if(Xsave->PC!=StrToU16(TempArray))
                                    {
                                     Xsave->PC=StrToU16(TempArray);
                                     Disyou(Xsave->PC);
                                     RRegs(Xsave);
                                    }
                                    break;
                           case 201:Xsave->S=StrToU8(TempArray);
                                    RRegs(Xsave);
                                    Disyou(tscroll);
                                    break;
                           case 202:Xsave->P&=0x30;
                                    Xsave->P|=StrToU8(TempArray)&~0x30;
                                    RFlags(Xsave);
                                    Disyou(tscroll);
                                    break;
                           case 210:Xsave->A=StrToU8(TempArray);
                                    RRegs(Xsave);
                                    Disyou(tscroll);
                                    break;
                           case 211:Xsave->X=StrToU8(TempArray);
                                    RRegs(Xsave);
                                    Disyou(tscroll);
                                    break;
                           case 212:Xsave->Y=StrToU8(TempArray);
                                    RRegs(Xsave);
                                    Disyou(tscroll);
                                    break;
                          }
                         }
                        }
                        break;

                 case BN_CLICKED:
                         if(LOWORD(wParam)>=400 && LOWORD(wParam)<=407)
                         {
                          if(0==instep)
                          {
                           Xsave->P^=1<<(LOWORD(wParam)&7);
                           RFlags(Xsave);
                          }
                         }
                         else switch(LOWORD(wParam))
                         {
                          case 300:
                           instep=1;
                           crs(hwndDlg);
                           break;
                          case 301:
                           instep=-1;
                           crs(hwndDlg);
                           break;
                          case 302:FCEUI_NMI();break;
                          case 303:FCEUI_IRQ();break;
                          case 310:FCEUI_ResetNES();
                                   if(instep>=0)
                                    instep=1;
                                   crs(hwndDlg);
                                   break;
                          case 311:DoMemmo(hwndDlg);break;

                          case 540:
                                   {
                                    LONG t;
                                    t=SendDlgItemMessage(hwndDlg,510,LB_GETCURSEL,0,0);
                                    if(t!=LB_ERR)
                                    {
                                     FCEUI_DeleteBreakPoint(t);
                                     SendDlgItemMessage(hWndCallB,510,LB_DELETESTRING,t,(LPARAM)(LPSTR)0);
                                    }
                                   }
                                   break;
                          case 541:
                                   {
                                    uint16 A1,A2;
                                    int type;
                                    FetchBPDef(hwndDlg, &A1, &A2, &type);
                                    FCEUI_AddBreakPoint(type, A1,A2,BPointHandler);
                                    hWndCallB=hwndDlg;  /* Hacky hacky. */
                                    RBPCallBack(type, A1,A2,0);
                                   }
                                   break;
                         }
                        break;
                }


                if(!(wParam>>16))
                switch(wParam&0xFFFF)
                {
                 case 1:
                        gornk:                        
                        instep=-1;
                        FCEUI_SetCPUCallback(0);
                        DestroyWindow(dwin);
                        dwin=0;
                        break;
                }
              }
  return 0;
}

void BeginDSeq(HWND hParent)
{
 if(dwin)
 {
  SetFocus(dwin);
  return;
 }
 if(!GI)
 {
  FCEUD_PrintError("You must have a game loaded before you can screw up a game.");
  return;
 }

 dwin=CreateDialog(fceu_hInstance,"DEBUGGER",0,DebugCon);
}

/* 16 numbers per line times 3 then minus one(no space at end) and plus
   6 for "8000: "-like string and plus 2 for crlf.  *16 for 16 lines, and
   +1 for a null.
*/
static uint8 kbuf[(16*3-1+6+2)*16+1];
static void sexycallb(uint16 a, uint8 v)
{
 if((a&15)==15)
  sprintf(kbuf+strlen(kbuf),"%02X\r\n",v);
 else if((a&15)==0)
  sprintf(kbuf+strlen(kbuf),"%03xx: %02X ",a>>4,v);
 else
  sprintf(kbuf+strlen(kbuf),"%02X ",v);
}

static void MDSSI(void)
{
 SCROLLINFO si;

 memset(&si,0,sizeof(si));
 si.cbSize=sizeof(si);
 si.fMask=SIF_ALL;
 si.nMin=0;
 si.nMax=0xFFFF>>4;
 si.nPage=16;
 cmsi=si.nPos=0;
 SetScrollInfo(GetDlgItem(mwin,103),SB_CTL,&si,1);
}

static void UpdateDMem(uint16 a)
{
 kbuf[0]=0;
 FCEUI_MemDump(a<<4,256,sexycallb);
 SetDlgItemText(mwin,100,kbuf);
}

int CreateDumpSave(uint32 a1, uint32 a2)
{
 const char filter[]="Raw dump(*.dmp)\0*.dmp\0";
 char nameo[2048];
 OPENFILENAME ofn;

 memset(&ofn,0,sizeof(ofn));
 ofn.lStructSize=sizeof(ofn);
 ofn.hInstance=fceu_hInstance;
 ofn.lpstrTitle="Dump Memory As...";
 ofn.lpstrFilter=filter;
 nameo[0]=0;
 ofn.lpstrFile=nameo;
 ofn.nMaxFile=256;
 ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
 if(GetSaveFileName(&ofn))
 {
  FCEUI_DumpMem(nameo,a1,a2);
  return(1);
 }
 return 0;
}

int LoadSave(uint32 a)
{
 const char filter[]="Raw dump(*.dmp)\0*.dmp\0";
 char nameo[2048];
 OPENFILENAME ofn;

 memset(&ofn,0,sizeof(ofn));
 ofn.lStructSize=sizeof(ofn);
 ofn.hInstance=fceu_hInstance;
 ofn.lpstrTitle="Load Memory...";
 ofn.lpstrFilter=filter;
 nameo[0]=0;
 ofn.lpstrFile=nameo;
 ofn.nMaxFile=256;
 ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
 if(GetOpenFileName(&ofn))
 {
  FCEUI_LoadMem(nameo,a);
  return(1);
 }
 return 0;
}

static BOOL CALLBACK MemCon(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  int32 scrollindex=0,ced=0;
  char TempArray[64];

  DSMFix(uMsg);

  switch(uMsg)
  {
   case WM_CLOSE:
   case WM_QUIT: goto gornk;

   case WM_COMMAND:
                switch(HIWORD(wParam))
                {
                 case BN_CLICKED:
                         switch(LOWORD(wParam))
                         {
                          case 203:
                          case 202:
                          {
                           uint16 a;
                           uint8 v;
                           GetDlgItemText(hwndDlg,200,TempArray,64);
                           a=StrToU16(TempArray);
                           GetDlgItemText(hwndDlg,201,TempArray,64);
                           v=StrToU8(TempArray);
                           FCEUI_MemPoke(a,v,LOWORD(wParam)&1);
                           UpdateDMem(cmsi);
                          
                           if(dwin && 0==instep)
                           {
                            int tscroll=32768+SendDlgItemMessage(dwin,101,SBM_GETPOS,0,0);
                            Disyou(tscroll);
                           }
                          }
                          break;
                          case 212:
                          {
                           uint16 a1;
                           uint16 a2;
                           GetDlgItemText(hwndDlg,210,TempArray,64);
                           a1=StrToU16(TempArray);
                           GetDlgItemText(hwndDlg,211,TempArray,64);
                           a2=StrToU16(TempArray);
                           CreateDumpSave(a1,a2);
                          }
                          break;
                          case 222:
                          {
                           uint16 a;
                           GetDlgItemText(hwndDlg,220,TempArray,64);
                           a=StrToU16(TempArray);
                           LoadSave(a);                        
                           UpdateDMem(cmsi);
                           if(dwin && 0==instep)
                           {
                            int tscroll=32768+SendDlgItemMessage(dwin,101,SBM_GETPOS,0,0);
                            Disyou(tscroll);
                           }
                          }
                          break;

                         }
                        break;
                }

                if(!(wParam>>16))
                switch(wParam&0xFFFF)
                {
                 case 1:
                        gornk:
                        DestroyWindow(mwin);
                        mwin=0;
                        break;
                }
                break;
   case WM_INITDIALOG:
                      return(1);
   case WM_VSCROLL:
                switch((int)LOWORD(wParam))
                {
                  case SB_TOP:
                        scrollindex=0;
                        ced=1;
                        break;
                  case SB_BOTTOM:
                        scrollindex=0xffff>>4;
                        ced=1;
                        break;
                  case SB_LINEUP:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,103),SB_CTL);
                        if(scrollindex>0)
                        {
                         scrollindex--;
                         ced=1;
                        }
                        break;

                  case SB_PAGEUP:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,103),SB_CTL);
                        scrollindex-=16;
                        if(scrollindex<0)
                         scrollindex=0;
                        ced=1;
                        break;

                  case SB_LINEDOWN:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,103),SB_CTL);
                        if(scrollindex<(0xFFFF>>4))
                        {
                         scrollindex++;
                         ced=1;
                        }
                        break;

                  case SB_PAGEDOWN:
                        scrollindex=GetScrollPos(GetDlgItem(hwndDlg,103),SB_CTL);
                        scrollindex+=16;
                        if(scrollindex>(0xFFFF>>4))
                        {
                         scrollindex=(65535>>4);
                        }
                        ced=1;
                        break;

                  case SB_THUMBPOSITION:
                  case SB_THUMBTRACK:
                        scrollindex=HIWORD(wParam);
                        ced=1;
                        break;
                 }
                 if(ced)
                 {
                  SendDlgItemMessage(hwndDlg,103,SBM_SETPOS,scrollindex,1);
                  UpdateDMem(scrollindex);
                  cmsi=scrollindex;
                 }
                 break;



  }

 return 0;
}

static void DoMemmo(HWND hParent)
{
 if(mwin)
 {
  SetFocus(mwin);
  return;
 }
 mwin=CreateDialog(fceu_hInstance,"MEMVIEW",0,MemCon);
 MDSSI();
 UpdateDMem(0);
}

void UpdateDebugger(void)
{
 if(mwin)
  UpdateDMem(cmsi);
}

void KillDebugger(void)
{
 if(mwin)
  DestroyWindow(mwin);
 if(dwin)
  DestroyWindow(dwin);
 dwin=mwin=0;
}
