#include "StdAfx.h"
#include "Commctrl.h"
#include "Commdlg.h"

#include "AutoBuild.h"
#include "GBA.h"
#include "Font.h"
#include "debugger.h"
#include "Sound.h"
#include "unzip.h"
#include "gb/GB.h"
#include "gb/gbGlobals.h"
#ifdef GP_EMULATION
#include "gp/GP.h"
#endif

#ifdef WIN32_PLATFORM_PSPC
#include "gx.h"
#endif

#include "time.h"
#include "CE.h"
#include "CEvideo.h"
#include "CEsound.h"
#include "CEkey.h"
#include "ppp_stdio.h"



extern void remoteInit();
extern void remoteCleanUp();
extern void remoteStubMain();
extern void remoteStubSignal(int,int);
extern void remoteOutput(char *, u32);
extern void remoteSetProtocol(int);
extern void remoteSetPort(int);
extern void debuggerOutput(char *, u32);

extern void CPUUpdateCPSR();

bool (*emuWriteState)(char *) = NULL;
bool (*emuReadState)(char *) = NULL;
bool (*emuWriteBattery)(char *) = NULL;
bool (*emuReadBattery)(char *) = NULL;
void (*emuReset)() = NULL;
void (*emuCleanUp)() = NULL;
bool (*emuWritePNG)(char *) = NULL;
bool (*emuWriteBMP)(char *) = NULL;
void (*emuMain)(int) = NULL;
void (*emuUpdateCPSR)() = NULL;
int  emuCount = 0;
bool emuHasDebugger = false;

static u8 COPYRIGHT[] = {
  0xa9, 0x96, 0x8c, 0x8a, 0x9e, 0x93, 0xbd, 0x90, 0x86, 0xbe, 0x9b, 0x89,
  0x9e, 0x91, 0x9c, 0x9a, 0xdf, 0xd7, 0xbc, 0xd6, 0xdf, 0xce, 0xc6, 0xc6,
  0xc6, 0xd3, 0xcd, 0xcf, 0xcf, 0xcf, 0xd3, 0xcd, 0xcf, 0xcf, 0xce, 0xdf,
  0x9d, 0x86, 0xdf, 0xb9, 0x90, 0x8d, 0x98, 0x90, 0x8b, 0x8b, 0x9a, 0x91,
  0x00
};

int systemSoundRate[5] = { 0,44100,22050,0,11025 };

int systemRedShift   =  0;
int systemBlueShift  = 11;
int systemGreenShift =  6;
int systemColorDepth = 16;
int systemDebug = 0;
int systemVerbose = 0;
int systemScrPosX = 0;
int systemScrPosY = 0;
int systemWidth = 240;

int cartridgeType = 3;
int captureFormat = 0;

int emulating = 0;
int statenumber = 1;
int RGB_LOW_BITS_MASK=0x821;
int systemFrameSkip = 0;
//u32 systemColorMap32[0x10000];
u16 systemColorMap16[0x10000];
u16 systemGbPalette[24];
char filename[2048];
char biosFileName[2048];
char captureDir[2048];
char saveDir[2048];
char batteryDir[2048];

bool threadctrl = true;

bool paused = false;
bool debugger = true;
bool debuggerStub = false;
bool systemSoundOn = true;
bool removeIntros = false;

bool AutoLoadState = true;

extern void debuggerSignal(int,int);

void (*dbgMain)() = debuggerMain;
void (*dbgSignal)(int,int) = debuggerSignal;
void (*dbgOutput)(char *, u32) = debuggerOutput;

extern bool CPUIsGBAImage(char *);
extern bool gbIsGameboyRom(char *);


/*************************************************************/
/*  */
/*************************************************************/
int frame_count = 0;
char ProgDir[512];

extern HINSTANCE g_hInst;
extern HWND      g_hWnd;
extern HWND      g_hWndCB;
extern HANDLE    GBA_Thread;
extern int thread_running;
extern int CommandBarVis;

/*************************************************************/
/* functions */
/*************************************************************/
void   TrashMachine(void);
int    OpenFileDlg(HWND hWnd, char * fname);
int    thread_bga(void);
int    start_gba(void);
char * GetModulePath(void);
char * ChgModuleExt(char * ext);
void   LoadOption(void);
void   SaveOption(void);

/*************************************************************/
/*************************************************************/
int InitMachine(void)
{
    CEVideo_SetVideoMode();

    if( cartridgeType == 1 ){ 
        if( !CEVideo_Init( 162, 144 ) ) return 0;
    }else{
        if( !CEVideo_Init( 240, 162 ) ) return 0;
    }
	LOG( "InitMachine OK\n");
    return (1);
}

/*************************************************************/
/*************************************************************/
void TrashMachine(void)
{
    CEKey_Trash();
}

/*************************************************************/
/*************************************************************/
void writeBatteryFile()
{
  char buffer[1048];
  char *p;

  strcpy( buffer,filename );
  p  = strrchr(buffer,'.');
  *p = '\0';
  strcat( buffer, ".sav");
  emuWriteBattery(buffer);
  LOG("Battery file saved.\n");
}

/*************************************************************/
/*************************************************************/
void readBatteryFile()
{
  char buffer[1048];
  char *p;
  bool res;

  strcpy( buffer,filename );
  p  = strrchr(buffer,'.');
  *p = '\0';
  strcat( buffer, ".sav");

  res = emuReadBattery(buffer);
  if(res){
    LOG("Battery file loaded.\n");
//    systemScreenMessage((char *)winResLoadString(IDS_LOADED_BATTERY));
  }
}

/*************************************************************/
/*************************************************************/
void loadSaveGame(int num)
{
  char buffer[1048];
  char *p;
  strcpy( buffer,filename );
  p  = strrchr(buffer,'.');
  *p = '\0';
  sprintf(buffer, "%s%d.sgm", buffer, num);

  emuReadState(buffer);
  LOG("Load state file.[%d]\n",num);
/*
  sprintf(winBuffer, winResLoadString(IDS_LOADED_STATE_N), num);
  systemScreenMessage(winBuffer);
*/
}

/*************************************************************/
/*************************************************************/
void writeSaveGame(int num)
{
  char buffer[1048];
  char *p;
  strcpy( buffer,filename );
  p  = strrchr(buffer,'.');
  *p = '\0';
  sprintf(buffer, "%s%d.sgm", buffer, num);

  emuWriteState(buffer);
  LOG("Save state file.[%d]\n",num);
/*
  sprintf(winBuffer, winResLoadString(IDS_WROTE_STATE_N), num);
  systemScreenMessage(winBuffer);
*/
}

/*************************************************************/
/*************************************************************/
int thread_gba(void)
{
//    int skip;
    bool failed = false;
    int i;
    bool found = false;
    unz_file_info info;
    char buffer[1024];

    LOG("GBA thread start\n");

//    systemFrameSkip = frameSkip = 2;
    gbBorderOn = 0;

    for(i = 0; i < 24;) {
        systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10);
        systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10);
        systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10);
        systemGbPalette[i++] = 0;
    }
    
    
    if( !OpenFileDlg(g_hWnd, filename ) ){
	    LOG("GBA thread end.\n");
		return 0;
	}
    DEBUGLOG("thread_gba 2\n");
    LOG("Select open file [%s]\n", filename );

    if(CPUIsZipFile(filename)) {
      unzFile unz = unzOpen(filename);
      
      if(unz == NULL) {
        systemMessage(0, "Cannot open file %s", filename);
        exit(-1);
      }
      int r = unzGoToFirstFile(unz);
      
      if(r != UNZ_OK) {
        unzClose(unz);
        systemMessage(0, "Bad ZIP file %s", filename);
        exit(-1);
      }
      
    
      while(true) {
        r = unzGetCurrentFileInfo(unz,
                                  &info,
                                  buffer,
                                  sizeof(buffer),
                                  NULL,
                                  0,
                                  NULL,
                                  0);
        
        if(r != UNZ_OK) {
          unzClose(unz);
          systemMessage(0,"Bad ZIP file %s", filename);
          exit(-1);
        }
        
        if(gbIsGameboyRom(buffer)) {
          found = true;
          cartridgeType = 1;
          break;
        }
        if(CPUIsGBAImage(buffer)) {
          found = true;
          cartridgeType = 0;
          break;
        }
        r = unzGoToNextFile(unz);
        if(r != UNZ_OK)
          break;
      }
      
      if(!found) {
        unzClose(unz);
        systemMessage(0, "No image found on ZIP file %s", filename);
        exit(-1);
      }
      
      unzClose(unz);
    }else{
        if( gbIsGameboyRom(filename) ){
            cartridgeType   = 1;
        }
        if( CPUIsGBAImage(filename) ){
            cartridgeType   = 0;
        }
    }
    if(!InitMachine() ){
        LOG("Initialize Machine error \n");
        DEBUGLOG("start_gba error end [InitMachine err]\n");
        return -1;
    }

    if(cartridgeType == 1) {
      DEBUGLOG("thread_gba 2-1\n");
      failed          = !gbLoadRom(filename);
      emuWriteState   = gbWriteSaveState;
      emuReadState    = gbReadSaveState;
      emuWriteBattery = gbWriteBatteryFile;
      emuReadBattery  = gbReadBatteryFile;
      emuReset        = gbReset;
      emuCleanUp      = gbCleanUp;
      emuWritePNG     = gbWritePNGFile;
      emuWriteBMP     = gbWriteBMPFile;
      emuMain         = gbEmulate;
      emuUpdateCPSR   = NULL;
      emuHasDebugger  = false;
      emuCount        = 70000/4;
	} else {
      DEBUGLOG("thread_gba 2-2\n");
      failed          = !CPULoadRom(filename);
      cartridgeType   = 0;
      emuWriteState   = CPUWriteState;
      emuReadState    = CPUReadState;
      emuWriteBattery = CPUWriteBatteryFile;
      emuReadBattery  = CPUReadBatteryFile;
      emuReset        = CPUReset;
      emuCleanUp      = CPUCleanUp;
      emuWritePNG     = CPUWritePNGFile;
      emuWriteBMP     = CPUWriteBMPFile;
      emuMain         = CPULoop;
      emuUpdateCPSR   = CPUUpdateCPSR;
      emuHasDebugger  = false;
      emuCount        = 50000;

      CPUInit(biosFileName, useBios);
      CPUReset();
    }
    DEBUGLOG("thread_gba 3\n");
   
    CommandBarVis = CommandBar_Show( g_hWndCB, FALSE );

  if(cartridgeType == 0) {
  } else if (cartridgeType == 1) {
    if(gbBorderOn) {
      gbBorderLineSkip = 256;
      gbBorderColumnSkip = 48;
      gbBorderRowSkip = 40;
    } else {      
      gbBorderLineSkip = 160;
      gbBorderColumnSkip = 0;
      gbBorderRowSkip = 0;
    }      
  } else {
  }
  DEBUGLOG("thread_gba 4\n");

  for(i = 0; i < 0x10000; i++) {
    systemColorMap16[i] =   ((i & 0x001f)        << systemBlueShift )|
                           (((i & 0x03e0) >>  5) << systemGreenShift )|
                           (((i & 0x7c00) >> 10) << systemRedShift  );  

  }

  DEBUGLOG("thread_gba 5\n");

  readBatteryFile();

  DEBUGLOG("thread_gba 6\n");

  if( AutoLoadState ) loadSaveGame(0);

  Sleep(1000);
  emulating = 1;

  LOG("Emulation Loop Start\n");
  
  while(emulating) {
    if(!paused) {
      if(debugger && emuHasDebugger)
        dbgMain();
      else
        DEBUGLOG("thread_gba 8\n");
        emuMain(emuCount);
        DEBUGLOG("thread_gba 8-1\n");
    }
    switch( emulating ){
        case 2 :
          DEBUGLOG("thread_gba 9\n");
          loadSaveGame(statenumber);
          emulating = 1;
          break;
        case 3 :
          DEBUGLOG("thread_gba 10\n");
          writeSaveGame(statenumber);
          emulating = 1;
          break;
    }
  }
//  fprintf(stderr,"Shutting down\n");
  LOG("Emulation loop exit\n");

  if( AutoLoadState ) writeSaveGame(0);

  if(gbRom != NULL || rom != NULL) {
    writeBatteryFile();
    emuCleanUp();
  }

  soundShutdown();

  CEVideo_Trash();

  emulating = -1;
  LOG("GBA thread end\n");
  return(0);
}

/*************************************************************/
/*************************************************************/
int start_gba(void)
{
	DWORD rc;


    GBA_Thread = CreateThread(NULL, 
		0,		// stack size (ignored)
		(LPTHREAD_START_ROUTINE)thread_gba,	// Thread function
		0,		// 32 bit parameter passed to thread
		0,		// Creation flags (0 = start thread immediately)
		&rc);	// Receives thread ID

    LOG("\n\nGBA thread create ");
    if( GBA_Thread == NULL ){
        LOG("failed!\n");
    }else{
        LOG("OK!\n");
    }
    return 0;
}


/*************************************************************/
/*************************************************************/
int OpenFileDlg(HWND hWnd, char * fname)
{
	TCHAR szFileName[256];
	TCHAR szDir[256];
	TCHAR filter[] = _T("GBA Files\0*.gba;*.zip\0GB Files\0*.gb;*.zip\0All files\0*.*\0"); 

	int len;
	
	int ret;
	OPENFILENAME ofn;

	DEBUGLOG( "OpenFileDlg Start\n" );

	szFileName[0] = 0;
	szDir[0] = 0;
	mbstowcs( szDir, GetModulePath() ,256 );	
	memset(&ofn,0,sizeof(ofn));

    DEBUGLOG( "OpenFileDlg 1\n" );

    ofn.lpstrFilter	= filter;
    ofn.hwndOwner   = hWnd;
    ofn.lStructSize	= sizeof(ofn);
    ofn.lpstrFile	= szFileName;
    ofn.nMaxFile	= 256;
    ofn.Flags		= OFN_EXPLORER | OFN_ENABLETEMPLATE;
	if(szDir[0]) ofn.lpstrInitialDir = szDir;

    DEBUGLOG( "OpenFileDlg 2\n" );
	// display a open file dialog

    HINSTANCE hInst = LoadLibrary(_T("gsgetfile.dll"));
	if (hInst) {
		BOOL (*gsGetOpenFileName)(OPENFILENAME* );
		(FARPROC&)gsGetOpenFileName = GetProcAddress(hInst, _T("gsGetOpenFileName"));
		if (gsGetOpenFileName) {
			ret = gsGetOpenFileName(&ofn);
		}
		FreeLibrary(hInst);
    }else{
        ret = GetOpenFileName(&ofn);
    }
    DEBUGLOG( "OpenFileDlg 3\n" );

	if( !ret ){
        fname = NULL;
      	LOG( "OpenFileDlg Cancel END\n" );
        return 0;
    }

	DEBUGLOG( "OpenFileDlg 4\n" );
    len   = wcslen(szFileName);
    wcstombs(fname, szFileName,len * 2 + 1 );
    
    DEBUGLOG( "OpenFileDlg OK END\n" );
    return 1;
}

/*************************************************************/
/*************************************************************/
void CheckBool( char * key, char * buf, bool * flg )
{
	if( !strncmp(key,buf,10) ){
		if( buf[11] == 't' || buf[11] == 'T' ) *flg = TRUE;
										else   *flg = FALSE;
	}
}

void CheckInt( char * key, char * buf, int * flg )
{
	if( !strncmp(key,buf,10) )	*flg =  atoi( &buf[11] );
}

void CheckByte( char * key, char * buf, unsigned char * flg )
{
	if( !strncmp(key,buf,10) )	*flg =  atoi( &buf[11] );
}

/*************************************************************/
/*************************************************************/
void LoadOption()
{
	FILE * fp;
	char buf[256];

	strcpy(buf, GetModulePath());
	strcat(buf,"\\VBAdvanceCE.cfg");

	fp = fopen( buf,"r" );
	while( fgets(buf,255,fp) ){
		CheckBool( "USESOUND  ",buf,&systemSoundOn    );
        CheckInt ( "SOUNDQTY  ",buf,&soundQuality     );
        CheckInt ( "FRAMESKIP ",buf,&systemFrameSkip  );
        CheckBool( "AUTOSTATE ",buf,&AutoLoadState    );
        CheckInt ( "VIDEOMODE ",buf,&videomode        );
        CheckInt ( "SCANLINE  ",buf,&scanline         );
		CheckInt ( "SCRPOSX   ",buf,&systemScrPosX    );
		CheckInt ( "SCRPOSY   ",buf,&systemScrPosY    );
		CheckBool( "THREADCTRL",buf,&threadctrl       );
    }
    fclose(fp);
    if(systemFrameSkip<1) systemFrameSkip = 1; 
}

/*************************************************************/
/*************************************************************/
void SaveOption()
{
	FILE * fp;
	char buf[256];

	strcpy(buf, GetModulePath());
	strcat(buf,"\\VBAdvanceCE.cfg");
    
	fp = fopen( buf,"w" );
	fprintf(fp,"USESOUND  =%s\n",(systemSoundOn ?"True":"False") );
    fprintf(fp,"SOUNDQTY  =%d\n",soundQuality   );
	fprintf(fp,"FRAMESKIP =%d\n",systemFrameSkip);
    fprintf(fp,"AUTOSTATE =%s\n",(AutoLoadState?"True":"False") );
    fprintf(fp,"VIDEOMODE =%d\n",videomode      );
    fprintf(fp,"SCANLINE  =%d\n",scanline       );
	fprintf(fp,"SCRPOSX   =%d\n",systemScrPosX  );
	fprintf(fp,"SCRPOSY   =%d\n",systemScrPosY  );
    fprintf(fp,"THREADCTRL=%s\n",(threadctrl?"True":"False") );
    fclose(fp);
}

/*************************************************************/
/*************************************************************/
char * GetModulePath(void)
{
    TCHAR cmd[512];
    char * bufptr;
    int ret;

    ret = GetModuleFileName(g_hInst,cmd,512);
    ret = wcstombs(ProgDir,cmd,512); 
    if ((bufptr=strrchr( ProgDir, '\\' )) != NULL )*bufptr = '\0';
    return ProgDir;
}

/*************************************************************/
/*************************************************************/
char * ChgModuleExt(char * ext)
{
    char * bufptr;
    char * bufptr2;
    bufptr = GetModulePath();
    bufptr2 = (char*) malloc( strlen(bufptr)+strlen(ext)+1 );
    strcat( bufptr2,ext );
    free( bufptr );
    return bufptr2;
}

void systemMessage(int number, char *defaultMsg, ...)
{
    char buf[2048];
    va_list valist;
    const char *msg = (const char *)defaultMsg;
// if(number)
//    msg = winResLoadString(number);
  
    va_start(valist, defaultMsg);
    vsprintf(buf, msg, valist);
    va_end(valist);
    strcat(buf,"\n");
    LOG( buf );
}

void systemShowSpeed(int speed)
{
}

void system10Frames(int rate)
{
}


void systemSetTitle(char *title)
{
}

bool systemReadJoypads()
{
  return true;
}

void systemWriteDataToSoundBuffer()
{
    CESound_Write(soundFinalWave ,soundBufferLen);
}

bool systemSoundInit()
{
    switch(soundQuality){
        case 1: soundBufferLen = 1470*2; break;
        case 2: soundBufferLen =  735*2; break;
        case 4: soundBufferLen =  368*2; break;
    }
    return (CESound_Init( systemSoundRate[soundQuality], 16, 2, soundBufferLen, 8 )==1);
}

void systemSoundShutdown()
{
    CESound_Trash();
}

void systemSoundPause()
{
}

void systemSoundResume()
{
}

void systemSoundReset()
{
    CESound_Trash();
    CESound_Init(systemSoundRate[soundQuality], 16, 2, soundBufferLen, 8 );
}

static int ticks = 0;

u32 systemGetClock()
{
  return ticks++;
}

void systemUpdateMotionSensor()
{
}

int systemGetSensorX()
{
  return 0;
}

int systemGetSensorY()
{
  return 0;
}

void systemGbPrint(u8 *data,int pages,int feed,int palette, int contrast)
{
}

void systemScreenMessage(char *msg)
{
}

bool systemCanChangeSoundQuality()
{
    return (CESound_status()==CESOUND_PLAY?true:false);
}

bool systemPauseOnFrame()
{
  return false;
}
void systemFrame()
{
}

u32 systemReadJoypad(int a)
{
    return CEKey_Update();
}

void debuggerSignal(int a,int b)
{
}

void debuggerOutput(char * str, u32 flg)
{
}

void debuggerMain()
{
}

time_t time( time_t * tim )
{
    time_t t;
    SYSTEMTIME st;
    GetLocalTime(&st);
    
    st.wYear -= 1900;
    t = (st.wYear   * 12 * 31 * 24 * 60 * 60 )+ 
        (st.wMonth  *      31 * 24 * 60 * 60 )+
        (st.wDay    *           24 * 60 * 60 )+
        (st.wHour   *                60 * 60 )+ 
        (st.wMinute *                     60 )+       
         st.wSecond; 

    if(tim) *tim = t;
    return t;
}

struct tm g_tm;

struct tm * __cdecl localtime(const time_t * tim )
{
    time_t wk;  
    wk = *tim; 
    g_tm.tm_sec  = wk % 60; wk = (int)(wk / 60);
    g_tm.tm_min  = wk % 60; wk = (int)(wk / 60);    /* minutes after the hour - [0,59] */
    g_tm.tm_hour = wk % 24; wk = (int)(wk / 24);    /* hours since midnight - [0,23] */
    g_tm.tm_mday = wk % 31; wk = (int)(wk / 31);    /* day of the month - [1,31] */
    g_tm.tm_mon  = wk % 12; wk = (int)(wk / 12);    /* months since January - [0,11] */
    g_tm.tm_year = wk;                              /* years since 1900 */

    g_tm.tm_wday  = 0;    /* days since Sunday - [0,6] */       /* not implement */
    g_tm.tm_yday  = 0;    /* days since January 1 - [0,365] */  /* not implement */
    g_tm.tm_isdst = 0;   /* daylight savings time flag */       /* not implement */

    return &g_tm;
}

