// WiFi test app
// test mod

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspwlan.h>
#include "libnet/pspnet.h"
#include <psppower.h>
#include <pspiofilemgr_fcntl.h>
#include <pspiofilemgr_dirent.h>
#include <string.h>
#include <stdio.h>
#include "pg.h"
#include "dialog.h"
#include "vncdbg.h"
#include "menu.h"

#ifdef DEVHOOK
PSP_MODULE_INFO("PSPVNC", 0, 1, 1);
#else
PSP_MODULE_INFO("PSPVNC", 0x1000, 1, 1);
#endif
/* Define the main thread's attribute value (optional) */
PSP_MAIN_THREAD_ATTR(0);
PSP_MAIN_THREAD_STACK_SIZE_KB(32); /* smaller stack for kernel thread */

//asm(".global __lib_stub_top");
//asm(".global __lib_stub_bottom");


#define MAXPATH 512
#define MAXNAME 256
#define MAX_ENTRY 2048

char VNCPath[MAXPATH];
char logpath[MAXPATH];

int bSleep = 0, bQuit = 0, logcnt = 0, bDisconnected = 0;

static void InitInet();
static void ShutdownInet();
void vncDisconnect();
void vncConnect();

extern u8 EnableLogging;
extern void LoadOptions();

void power_callback(int unknown, int pwrflags)
{
        if(pwrflags & PSP_POWER_CB_POWER_SWITCH){
          vncDisconnect();
          if(g_fdLog)
            LogClose();
          bSleep=1;
        }else if(pwrflags & PSP_POWER_CB_RESUME_COMPLETE){
          if(EnableLogging) {
            sprintf(logpath, "%svnclog%i.txt", VNCPath, logcnt++);
            LogOpen(logpath);
          }
          bSleep=0;
          bDisconnected=1;
        }
        int cbid = sceKernelCreateCallback("Power Callback", power_callback, NULL);
        scePowerRegisterCallback(0, cbid);
}

static int exit_callback(int arg1, int arg2, void *common)
{
        sceKernelExitGame();
        return 0;
}

void CallbackThread(void *arg)
{
        int cbid;
 
        // Install Callbacks
	sceKernelRegisterExitCallback(sceKernelCreateCallback("Exit Callback", exit_callback, NULL));
        cbid = sceKernelCreateCallback("Power Callback", power_callback, NULL);
        scePowerRegisterCallback(0, cbid);
 
        // Poll them
        sceKernelSleepThreadCB();
}

int SetupCallbacks(void)
{       
        int thid = 0;
 
        thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, THREAD_ATTR_USER, 0);
        if(thid >= 0)
                sceKernelStartThread(thid, 0, 0);

        return thid;
}

#ifndef DEVHOOK
void MyExceptionHandler(PspDebugRegBlock *regs)
{
        /* Do normal initial dump, setup screen etc */
        pspDebugScreenInit();

        /* I always felt BSODs were more interesting that white on black */
        pspDebugScreenSetBackColor(0x00000000);
        pspDebugScreenSetTextColor(0xFFFFFFFF);
        pspDebugScreenClear();

        pspDebugScreenPrintf("Exception Details:\n");
        pspDebugDumpException(regs);

        pspDebugScreenPrintf("Press X to reboot.\n");
        
        while(!(new_pad & PSP_CTRL_CROSS))
          readpad(1);
          
        sceKernelExitGame();
}

static void logkprintf(void *format, u32 *args) {
  LogPrintf(format, args[0], args[1], args[2], args[3]);
}
#endif

// see startup.s -- a kernel mode thread !!
// needed for flash0: LoadModule and kmem dump

void
user_thread(SceSize args, void *argp)
{
  int err = nlhInit();
  LogPn1("nlhInit returns ", err, "\n");
  if (err != 0) bQuit=1;

  while(!bQuit) {
    InitInet();
    vncMenu();
    ShutdownInet();
  }
 
  PostProgressMessage("\tStopping NetLib", 2, 4);
  nlhTerm();
  sceKernelDelayThread(1000000); 

}


int 
main(int argc, char **argv)
{
    char *p;

    strcpy(VNCPath, argv[0]);
    p = strrchr(VNCPath, '/');
    *++p=0;

    {
      char splashpath[MAXPATH];
      int splashfd;
      sprintf(splashpath, "%ssplash.16", VNCPath);
      splashfd = sceIoOpen(splashpath, PSP_O_RDONLY, 0777);
      if(splashfd) {
        sceIoRead(splashfd, _splashbmp, 480*272*2);
        sceIoClose(splashfd);
      }
    }

    sprintf(logpath, "%svnclog%i.txt", VNCPath, logcnt++);

#ifndef DEVHOOK
    pspDebugInstallErrorHandler(MyExceptionHandler);
    pspDebugInstallKprintfHandler(logkprintf);
#endif

    pspDebugScreenInit();

	  sceCtrlSetSamplingCycle(0);
	  sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);

    pspDebugScreenPrintf("PSPVNC v1.2.2\n");
    pspDebugScreenPrintf("Please wait now loading !\n");
    sceKernelDelayThread(2000000); 

    pgInit();

    PostProgressMessage("", 0, 0);

    pgWaitVn(120);

    if(EnableLogging) {
      PostProgressMessage("\tLog Open", 0, 7);
      LogOpen(logpath);
    }

    LogPn1("Starting VNC Viewer\nthreadid ", sceKernelGetThreadId(), "\n");

    PostProgressMessage("\tCallbacks", 1, 7);
    SetupCallbacks();

    PostProgressMessage("\tWiFi Load", 2, 7);
    if (nlhLoadDrivers(&module_info) != 0)
    {
      MsgPrintf(0, "\tError loading WiFi drivers\n\n\nPlease check your settings\n\nand try again.");
      LogPrintf("Net driver load error\n");
    } 

  int user_thid = sceKernelCreateThread( "user_thread", 
     (SceKernelThreadEntry)user_thread, 0x8, 256*1024, 
     PSP_THREAD_ATTR_USER, 0 );
  if(user_thid >= 0) {
    sceKernelStartThread(user_thid, 0, 0);
    sceKernelWaitThreadEnd(user_thid, NULL);
  }

// Different progress set from before
    if(g_fdLog) {
      PostProgressMessage("\tLog Close", 3, 4);
      LogClose();
    }

    PostProgressMessage("\tExiting", 4, 4);

    sceGuTerm();

    sceKernelExitGame();

    return 0;
}

static char WiFiStateMessage[5][32] = {
  "\tWiFi Idle",
  "\tWiFi Init",
  "\tWiFi Connect",
  "\tWiFi DHCP",
  "\tWiFi Ready"
};

static void ShutdownInet() {
    int err;

    PostProgressMessage("\tStopping WiFi", 1, 4);
    err = sceNetApctlDisconnect();
    LogPn1("sceNetApctlDisconnect returns ", err, "\n");
    return;
}

extern int pickConnection(); 

static void InitInet()
{
    int net;
    int last_state;
    u32 err;
    u32 state, ostate=0, statechange=0;
    char szMyIPAddr[32];

    bDisconnected = 0;

  //LUDO:
	while (sceWlanGetSwitchState() != 1)	//switch = off
	{
    MsgPrintf(0, "\tTurn the wifi switch on !\n");
		sceKernelDelayThread(1000*1000);
	}

retry_net:
    net = pickConnection();
    if(net<0) {
      bQuit = 1;
      return;
    }

    // try first connection
    err = sceNetApctlConnect(net); // how to pick this ?
    LogPn1("sceNetApctlConnect returns ", err, "\n");
    if (err != 0)
        goto error_wifi;

    state = 0;
    err = sceNetApctlGetState(&state);
    LogPn1("getstate: err=", err, ", ");
    LogPn1("state=", state, "\n");
    if (err != 0)
        goto error_connection;

    last_state = -1;

    // 4 connected with IP address
    while (1)
    {
      err = sceNetApctlGetState(&state);
      if (err != 0)
      {
        LogPn1("sceNetApctlGetState returns ", err, "\n");
        goto close_connection;
      }

      {
        SceCtrlData c;
        sceCtrlReadBufferPositive(&c, 1);
        if (c.Buttons & PSP_CTRL_SQUARE) goto close_net;
      }


      if (state != last_state) 
      {
        if (last_state == 2 && state == 0) {
				 sceKernelDelayThread(500*1000); // 500ms
				 goto retry_net;
        }

        last_state = state;
      }

      PostProgressMessage(WiFiStateMessage[state], state, 4);

      readpad(0);
        
        // 0 - idle
        // 1,2 - starting up
        // 3 - waiting for dynamic IP -- REVIEW: wedges with DHCP
        // 4 - got IP - usable
      if (state == 4) break;  // connected with static IP
    }

    if(!bQuit) {
      // get IP address
      if (sceNetApctlGetInfo(8, szMyIPAddr) != 0)
          strcpy(szMyIPAddr, "unknown IP address");

      LogPrintf("sceNetApctlGetInfo #8: ipaddr=%s\n",szMyIPAddr);
    }

    return;

close_connection:
    PostProgressMessage("\tTerminating WiFi", 1, 4);
    err = sceNetApctlDisconnect();
    LogPn1("sceNetApctlDisconnect returns ", err, "\n");
    goto retry_net;

error_timeout:
    MsgPrintf(0, "\tWiFi Connection Timed Out\n\n\nPlease check the WiFi switch\n\nand try again.");
    goto close_connection;

error_connection:
    MsgPrintf(0, "\tAn error occured while connecting\n\nto the access point.\n\n\nPlease check your settings\n\nand try again.");
    goto close_connection;

error_wifi:
    MsgPrintf(0, "\tError Connecting to Network\n\n\nPlease check your settings\n\nand try again.");
    goto retry_net;

close_net:
  err = sceNetApctlDisconnect();

  nlhTerm();
  sceKernelDelayThread(1000000); 

}


