/*
 *  Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com)
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <fcntl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>

#include <pspctrl.h>
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspiofilemgr.h>

#include "psp_global.h"
#include "psp_sdl.h"
#include "psp_danzeff.h"
#include "psp_fmgr.h"
#include "psp_menu.h"
#include "psp_login.h"
#include "psp_battery.h"
#include "psp_help.h"

# define MENU_LOGIN_HOST         0
# define MENU_LOGIN_USER         1
# define MENU_LOGIN_PASSWORD     2
# define MENU_LOGIN_PORT         3
# define MENU_LOGIN_CHECK_SSH2   4
# define MENU_LOGIN_ID_RSA       5
# define MENU_LOGIN_CLOCK        6

# define MENU_LOGIN_HELP         7

# define MENU_LOGIN_NEW          8
# define MENU_LOGIN_DELETE       9

# define MENU_LOGIN_LOAD        10
# define MENU_LOGIN_SAVE        11

# define MENU_LOGIN_CONNECT     12

# define MENU_LOGIN_EXIT        13

# define MAX_MENU_LOGIN_ITEM (MENU_LOGIN_EXIT + 1)

  static menu_item_t menu_list[] =
  { 
    "Host     : ",
    "User     : ",
    "Password : ",
    "Port     : ",

    "Check remote SSH2 : ",
    "Local id_rsa key  : ",
    "PSP CPU clock     : ",

    "Help",

    "New",
    "Delete",
    "Load",
    "Save",
    "Connect",
    "Exit"
  };

  static int cur_menu_id = MENU_LOGIN_CONNECT;
  static int cur_slot    = 0;

  static int psp_cpu_clock = 222;
 
  static psplogin_list_t *loc_head_login_list = NULL;

  psplogin_list_t *psp_login_current  = NULL;

static void
psp_del_all_login_list()
{
  psplogin_list_t *del_list;
  while (loc_head_login_list != (psplogin_list_t *)0) 
  {
    del_list = loc_head_login_list;
    loc_head_login_list = del_list->next;
    free(del_list);
  }
  psp_login_current  = NULL;
}

static int
psp_save_login_list()
{
  gzFile          *file_desc;
  psplogin_list_t *scan_list;
  char             buffer[sizeof(psplogin_t)];
  int              error = 0;
  int              index = 0;

  file_desc = gzopen("./login.dat", "w");

  if (! file_desc) {
    return 1;
  }
  for (scan_list  = loc_head_login_list;
       scan_list != (psplogin_list_t *)0;
       scan_list  = scan_list->next) {

    /* Encode data */
    memcpy(buffer, &scan_list->login, sizeof(psplogin_t));
    for (index = 0; index < sizeof(psplogin_t); index++) {
      unsigned char encode = buffer[index];
      encode = (((encode >> 4) & 0x0F) | ((encode << 4) & 0xF0));
      buffer[index] = encode;
    }
    if (gzwrite(file_desc, buffer, sizeof(psplogin_t)) != sizeof(psplogin_t)) {
      error = 1;
    }
  }
  gzclose(file_desc);

  return error;
}

static void
psp_default_login_list()
{
  psplogin_list_t *new_list;

  new_list = malloc(sizeof(psplogin_list_t));
  new_list->next = loc_head_login_list;
  loc_head_login_list = new_list;
  strcpy(new_list->login.user, "");
  strcpy(new_list->login.password, "");
  strcpy(new_list->login.host, "");
  strcpy(new_list->login.port, "22");
  psp_login_current = new_list;
}

# if 0//LUDO: DEBUG
static void
psp_login_display_list()
{
  psplogin_list_t *scan_list;
fprintf(stdout, "%s:%d > scan\n", __FILE__, __LINE__);
  for (scan_list  = loc_head_login_list;
       scan_list != (psplogin_list_t*)0;
       scan_list  = scan_list->next) {
fprintf(stdout, "%s:%d scan %s\n", __FILE__, __LINE__, scan_list->login.host);
  }
fprintf(stdout, "%s:%d < scan\n", __FILE__, __LINE__);
}
# endif

static int
psp_load_login_list()
{
  gzFile          *file_desc;
  psplogin_t       new_login;
  psplogin_list_t *new_list;
  psplogin_list_t *scan_list;
  psplogin_list_t *prev_list;
  psplogin_list_t *next_list;
  char             buffer[sizeof(psplogin_t)];
  int              error = 0;
  int              index = 0;

  psp_del_all_login_list();

  psp_login_current = NULL;
  file_desc = gzopen("./login.dat", "r");

  if (file_desc) {

    while (gzread(file_desc, &buffer, sizeof(psplogin_t)) > 0) {
      new_list = malloc(sizeof(psplogin_list_t));

      /* Decode data */
      for (index = 0; index < sizeof(psplogin_t); index++) {
        unsigned char encode = buffer[index];
        encode = (((encode >> 4) & 0x0F) | ((encode << 4) & 0xF0));
        buffer[index] = encode;
      }
      memcpy(&new_list->login, buffer, sizeof(psplogin_t));
      new_list->next = loc_head_login_list;
      loc_head_login_list = new_list;
    }
    gzclose(file_desc);
  }

  /* Reverse list */
  if (loc_head_login_list) {
    scan_list = loc_head_login_list;
    next_list = scan_list->next;
    prev_list = NULL;
    while (next_list != NULL) {
      scan_list->next = prev_list;
      prev_list = scan_list;
      scan_list = next_list;
      next_list = scan_list->next;
    }
    scan_list->next = prev_list;
    loc_head_login_list = scan_list;
  }

  psp_login_current = loc_head_login_list;

  if (! psp_login_current) {
    psp_default_login_list();
  }

  return error;
}


static void 
psp_display_screen_menu(void)
{
  char buffer[64];
  int menu_id = 0;
  int slot_id = 0;
  int color   = 0;
  int x       = 0;
  int y       = 0;
  int y_step  = 0;

  {
    psp_sdl_blit_login();

    psp_sdl_draw_rectangle(10,10,459,249,PSP_MENU_BORDER_COLOR,0);
    psp_sdl_draw_rectangle(11,11,457,247,PSP_MENU_BORDER_COLOR,0);

    psp_sdl_back_print( 30, 6, " Login ", PSP_MENU_NOTE_COLOR);

    psp_display_screen_menu_battery();

    psp_sdl_back_print(30, 254, " []: Cancel  O/X: Valid ", 
                       PSP_MENU_BORDER_COLOR);

    psp_sdl_back_print(370, 254, " By Zx-81 ",
                       PSP_MENU_AUTHOR_COLOR);
  }
  
  x      = 20;
  y      = 25;
  y_step = 10;
  
  for (menu_id = 0; menu_id < MAX_MENU_LOGIN_ITEM; menu_id++) {
    color = PSP_MENU_TEXT_COLOR;
    if (cur_menu_id == menu_id) color = PSP_MENU_SEL_COLOR;
    else 
    if (menu_id == MENU_LOGIN_EXIT) color = PSP_MENU_WARNING_COLOR;
    else
    if (menu_id == MENU_LOGIN_HELP) color = PSP_MENU_GREEN_COLOR;

    psp_sdl_back_print(x, y, menu_list[menu_id].title, color);

    if (menu_id == MENU_LOGIN_HOST) {
      sprintf(buffer, "%s", psp_login_current->login.host);
      string_fill_with_space(buffer, MAX_HOST_LENGTH);
      psp_sdl_back_print(110, y, buffer, color);
    } else
    if (menu_id == MENU_LOGIN_USER) {
      sprintf(buffer, "%s", psp_login_current->login.user);
      string_fill_with_space(buffer, MAX_USER_LENGTH);
      psp_sdl_back_print(110, y, buffer, color);
    } else
    if (menu_id == MENU_LOGIN_PASSWORD) {
      int length = strlen(psp_login_current->login.password);
      memset(buffer, '*', length);
      buffer[length] = 0;
      string_fill_with_space(buffer, MAX_PASSWORD_LENGTH);
      psp_sdl_back_print(110, y, buffer, color);
    } else
    if (menu_id == MENU_LOGIN_PORT) {
      sprintf(buffer, "%s", psp_login_current->login.port);
      string_fill_with_space(buffer, MAX_PORT_LENGTH);
      psp_sdl_back_print(110, y, buffer, color);
      y += y_step;
    } else
    if (menu_id == MENU_LOGIN_CHECK_SSH2) {
      sprintf(buffer, "%s", PSPSSH.ssh_check_version ? "yes" : "no");
      string_fill_with_space(buffer, MAX_PORT_LENGTH);
      psp_sdl_back_print(180, y, buffer, color);
    } else
    if (menu_id == MENU_LOGIN_ID_RSA) {
      sprintf(buffer, "%s", PSPSSH.ssh_enable_id_rsa ? "yes" : "no");
      string_fill_with_space(buffer, MAX_PORT_LENGTH);
      psp_sdl_back_print(180, y, buffer, color);
    } else
    if (menu_id == MENU_LOGIN_CLOCK) {
      sprintf(buffer,"%d", psp_cpu_clock);
      string_fill_with_space(buffer, 4);
      psp_sdl_back_print(180, y, buffer, color);
      y += y_step;
    } else
    if (menu_id == MENU_LOGIN_HELP) {
      y += y_step;
    } else
    if (menu_id == MENU_LOGIN_DELETE) {
      y += y_step;
    } else
    if (menu_id == MENU_LOGIN_SAVE) {
      y += y_step;
    } else
    if (menu_id == MENU_LOGIN_CONNECT) {
      y += y_step;
    }

    y += y_step;
  }
}

static int
psp_login_save_config()
{
  char  FileName[MAX_PATH+1];
  FILE* FileDesc;
  int   error;

  snprintf(FileName, MAX_PATH, "%s/pspssh.cfg", PSPSSH.psp_homedir);
  error = 0;
  FileDesc = fopen(FileName, "w");
  if (FileDesc != (FILE *)0 ) {
    fprintf(FileDesc, "ssh_check_version=%d\n", PSPSSH.ssh_check_version);
    fprintf(FileDesc, "ssh_enable_id_rsa=%d\n", PSPSSH.ssh_enable_id_rsa);
    fprintf(FileDesc, "psp_cpu_clock=%d\n", PSPSSH.psp_cpu_clock);
    fclose(FileDesc);

  } else {
    error = 1;
  }

  return error;
}

static int
psp_login_load_config()
{
  char  Buffer[512];
  char  FileName[MAX_PATH+1];
  char *Scan;
  FILE* FileDesc;
  int   error;
  unsigned int Value;

  snprintf(FileName, MAX_PATH, "%s/pspssh.cfg", PSPSSH.psp_homedir);
  error = 0;
  FileDesc = fopen(FileName, "r");
  if (FileDesc == (FILE *)0 ) return 1;

  while (fgets(Buffer,512, FileDesc) != (char *)0) {

    Scan = strchr(Buffer,'\n');
    if (Scan) *Scan = '\0';
    /* For this #@$% of windows ! */
    Scan = strchr(Buffer,'\r');
    if (Scan) *Scan = '\0';
    if (Buffer[0] == '#') continue;

    Scan = strchr(Buffer,'=');
    if (! Scan) continue;

    *Scan = '\0';
    Value = atoi(Scan+1);

    if (!strcasecmp(Buffer,"ssh_check_version")) PSPSSH.ssh_check_version = Value;
    else
    if (!strcasecmp(Buffer,"ssh_enable_id_rsa")) PSPSSH.ssh_enable_id_rsa = Value;
    else
    if (!strcasecmp(Buffer,"psp_cpu_clock")) PSPSSH.psp_cpu_clock = Value;
  }

  fclose(FileDesc);

  scePowerSetClockFrequency(PSPSSH.psp_cpu_clock, PSPSSH.psp_cpu_clock, PSPSSH.psp_cpu_clock/2);

  return 0;
}

static void
psp_login_menu_save()
{
  int error;

  error  = psp_save_login_list();
  error |= psp_login_save_config();

  if (! error) /* save OK */
  {
    psp_display_screen_menu();
    psp_sdl_back_print(270,  80, "File saved !", PSP_MENU_NOTE_COLOR);
    psp_sdl_flip();
    sleep(1);
  }
  else 
  {
    psp_display_screen_menu();
    psp_sdl_back_print(270,  80, "Can't save file !", 
                       PSP_MENU_WARNING_COLOR);
    psp_sdl_flip();
    sleep(1);
  }
}

static void
psp_login_menu_load()
{
  int error;

  error = psp_load_login_list();

  if (! error) /* save OK */
  {
    psp_display_screen_menu();
    psp_sdl_back_print(270,  80, "File loaded !", PSP_MENU_NOTE_COLOR);
    psp_sdl_flip();
    sleep(1);
  }
  else 
  {
    psp_display_screen_menu();
    psp_sdl_back_print(270,  80, "Can't load file !", 
                       PSP_MENU_WARNING_COLOR);
    psp_sdl_flip();
    sleep(1);
  }
}

static void
psp_login_menu_delete()
{
  psplogin_list_t  *scan_list;
  psplogin_list_t **prev_list;

  scan_list = loc_head_login_list;
  prev_list = &loc_head_login_list;

  while (scan_list != 0) {

    if (scan_list == psp_login_current) {
      *prev_list = scan_list->next;
      psp_login_current = scan_list->next;
      if (psp_login_current == NULL) {
        if (loc_head_login_list != NULL) {
          psp_login_current = loc_head_login_list;
        } else {
          psp_default_login_list();
        }
      }
      free(scan_list);
      break;
    }
    prev_list = &scan_list->next;
    scan_list = scan_list->next;
  }

}

static void
psp_login_menu_new()
{
  psplogin_list_t *new_list;

  new_list = malloc(sizeof(psplogin_list_t));
  new_list->next = loc_head_login_list;
  loc_head_login_list = new_list;
  strcpy(new_list->login.user, "");
  strcpy(new_list->login.password, "");
  strcpy(new_list->login.host, "");
  strcpy(new_list->login.port, "22");
  psp_login_current = new_list;
}

int
psp_login_menu_exit(void)
{
  SceCtrlData c;

  psp_display_screen_menu();
  psp_sdl_back_print(270,  80, "press X to confirm !", PSP_MENU_WARNING_COLOR);
  psp_sdl_flip();

  psp_kbd_wait_no_button();

  do
  {
    sceCtrlReadBufferPositive(&c, 1);
    c.Buttons &= PSP_ALL_BUTTON_MASK;

    if (c.Buttons & PSP_CTRL_CROSS) {
      psp_sdl_exit(0);
    }

  } while (c.Buttons == 0);

  psp_kbd_wait_no_button();

  return 0;
}

static void
psp_login_menu_validate(void)
{
  /* Validate */
  PSPSSH.psp_cpu_clock = psp_cpu_clock;
  scePowerSetClockFrequency(PSPSSH.psp_cpu_clock, PSPSSH.psp_cpu_clock, PSPSSH.psp_cpu_clock/2);
}

int
psp_login_connect()
{
  if (cli_main_open_session()) {
    return 1;
  }
  return 0;
}

void
psp_login_menu_connect()
{
  int error = 0;

  if ( (!psp_login_current->login.user[0]) ||
       (!psp_login_current->login.password[0]) ||
       (!psp_login_current->login.host[0]) ||
       (!psp_login_current->login.port[0])) {

    psp_display_screen_menu();
    psp_sdl_back_print(250,  80, "Invalid parameters !", PSP_MENU_WARNING_COLOR);
    psp_sdl_flip();
    sleep(1);
    return;
  }

  psp_login_menu_validate();

  psp_display_screen_menu();
  psp_sdl_back_print(250,  80,   "trying to connect ... ", PSP_MENU_NOTE_COLOR);
  psp_sdl_flip();

  error = psp_login_connect();

  psp_display_screen_menu();

  if (error) {
    psp_sdl_back_print(250,  80, "connection failed ... ", PSP_MENU_WARNING_COLOR);
    psp_sdl_flip();
    sceKernelDelayThread(2000000);
    return;

  } 

  psp_sdl_back_print(250,  80, "connection succeed ... ", PSP_MENU_NOTE_COLOR);
  psp_sdl_flip();
  
  /* Initialize vt100 emulation */
  psp_vt100_init();

  psp_ssh_start_main_thread();
  sceKernelDelayThread(3000000);
    
  psp_ssh_main();
}

void
psp_login_put_key(int key)
{
  char *scan_str   = NULL;
  int   length     = 0;
  int   max_length = 0;

  switch (cur_menu_id) 
  {
    case MENU_LOGIN_HOST     : scan_str = psp_login_current->login.host;
                               max_length = MAX_HOST_LENGTH;
    break;
    case MENU_LOGIN_USER     : scan_str = psp_login_current->login.user;
                               max_length = MAX_USER_LENGTH;
    break;
    case MENU_LOGIN_PASSWORD : scan_str = psp_login_current->login.password;
                               max_length = MAX_PASSWORD_LENGTH;
    break;
    case MENU_LOGIN_PORT     : scan_str = psp_login_current->login.port;
                               max_length = MAX_PORT_LENGTH;
    break;
  }
 
  length = strlen(scan_str);
  if ((key == DANZEFF_DEL ) || 
      (key == DANZEFF_LEFT)) {
    if (length) {
      length--;
      scan_str[length] = '\0';
    }
  } else {
  
    if (cur_menu_id == MENU_LOGIN_PORT) {
      if ((key < '0') || (key > '9')) return;
    }
    if ((length + 1) >= max_length) return;
    scan_str[length] = key;
    length++;
    scan_str[length] = 0;
  }
}

static void
psp_login_go_right()
{
  psp_login_current = psp_login_current->next;
  if (psp_login_current == NULL) psp_login_current = loc_head_login_list;
}

static void
psp_login_go_left()
{
  int              found = 0;
  psplogin_list_t *scan_list;

  for (scan_list        = loc_head_login_list;
       scan_list->next != (psplogin_list_t *)0;
       scan_list        = scan_list->next) {
    if (scan_list->next == psp_login_current) {
      psp_login_current = scan_list;
      found = 1;
      break;
    }
  }
  if (! found) {
    psp_login_current = scan_list;
  }
}

static void
psp_login_menu_clock(int step)
{
  if (step > 0) {
    if (psp_cpu_clock == 222) {
      psp_cpu_clock = 300;
    } else if (psp_cpu_clock == 300) {
      psp_cpu_clock = 333;
    } else {
      psp_cpu_clock = 222;
    }
  } else {
    if (psp_cpu_clock == 222) {
      psp_cpu_clock = 333;
    } else if (psp_cpu_clock == 333) {
      psp_cpu_clock = 300;
    } else {
      psp_cpu_clock = 222;
    }
  }
}

static void
psp_login_menu_init(void)
{
  psp_cpu_clock = PSPSSH.psp_cpu_clock;
}

int 
psp_login_menu(void)
{
  int  danzeff_mode;
  int  danzeff_key;

  SceCtrlData c;
  long        new_pad;
  long        old_pad;
  int         last_time;
  int         end_menu;

  psp_login_load_config();
  psp_load_login_list();

  psp_login_menu_init();

  psp_kbd_wait_no_button();

  old_pad      = 0;
  last_time    = 0;
  end_menu     = 0;
  danzeff_mode = 0;

  while (! end_menu)
  {
    psp_display_screen_menu();

    if (danzeff_mode) {
      danzeff_moveTo(-50, -50);
      danzeff_render();
    }
    psp_sdl_flip();

    sceCtrlReadBufferPositive(&c, 1);
    c.Buttons &= PSP_ALL_BUTTON_MASK;

    new_pad = c.Buttons;

    if ((old_pad != new_pad) || ((c.TimeStamp - last_time) > PSP_MENU_MIN_TIME)) {
      last_time = c.TimeStamp;
      old_pad = new_pad;

    } else continue;

    if (danzeff_mode) {

      danzeff_key = danzeff_readInput(c);

      if (danzeff_key > DANZEFF_START) {
        if (danzeff_key > ' ') {
          psp_login_put_key(danzeff_key);
        } else
        if ((danzeff_key == DANZEFF_DEL ) ||
            (danzeff_key == DANZEFF_LEFT)) {
          psp_login_put_key(DANZEFF_DEL);
        } else
        if ((danzeff_key == DANZEFF_ENTER) ||
            (danzeff_key == DANZEFF_DOWN )) {
          cur_menu_id = cur_menu_id + 1;
          if (cur_menu_id > MENU_LOGIN_PORT) cur_menu_id = MENU_LOGIN_HOST;
        } else
        if (danzeff_key == DANZEFF_UP) {
          cur_menu_id = cur_menu_id - 1;
          if (cur_menu_id < 0) cur_menu_id = MENU_LOGIN_PORT;
        }

      } else 
      if ((danzeff_key == DANZEFF_START ) || 
          (danzeff_key == DANZEFF_SELECT)) 
      {
        danzeff_mode = 0;
        old_pad = new_pad = 0;

        psp_kbd_wait_no_button();
      }

      continue;
    }

    if ((c.Buttons & (PSP_CTRL_LTRIGGER|PSP_CTRL_RTRIGGER|PSP_CTRL_START)) ==
        (PSP_CTRL_LTRIGGER|PSP_CTRL_RTRIGGER|PSP_CTRL_START)) {
      /* Exit ! */
      psp_sdl_exit(0);
    } else
    if(new_pad & PSP_CTRL_START) {

      danzeff_mode = 1;
      if (cur_menu_id >= MENU_LOGIN_HELP) {
        cur_menu_id = MENU_LOGIN_HOST;
      }

    } else
    if ((new_pad & PSP_CTRL_CROSS ) || 
        (new_pad & PSP_CTRL_CIRCLE))
    {
      switch (cur_menu_id ) 
      {
        case MENU_LOGIN_CHECK_SSH2 : PSPSSH.ssh_check_version = ! PSPSSH.ssh_check_version;
        break;
        case MENU_LOGIN_ID_RSA     : PSPSSH.ssh_enable_id_rsa = ! PSPSSH.ssh_enable_id_rsa;
        break;

        case MENU_LOGIN_CLOCK     : psp_login_menu_clock( 1 );
        break;

        case MENU_LOGIN_NEW       : psp_login_menu_new();
        break;

        case MENU_LOGIN_DELETE    : psp_login_menu_delete();
        break;

        case MENU_LOGIN_CONNECT   : psp_login_menu_connect();
        break;

        case MENU_LOGIN_SAVE      : psp_login_menu_save();
        break;

        case MENU_LOGIN_LOAD      : psp_login_menu_load();
        break;

        case MENU_LOGIN_EXIT      : psp_login_menu_exit();
        break;

        case MENU_LOGIN_HELP      : psp_help_menu();
                                    old_pad = new_pad = 0;
        break;              
      }

    } else
    if(new_pad & PSP_CTRL_UP) {

      if (cur_menu_id > 0) cur_menu_id--;
      else                 cur_menu_id = MAX_MENU_LOGIN_ITEM-1;

    } else
    if(new_pad & PSP_CTRL_DOWN) {

      if (cur_menu_id < (MAX_MENU_LOGIN_ITEM-1)) cur_menu_id++;
      else                                 cur_menu_id = 0;

    } else
    if(new_pad & PSP_CTRL_RIGHT) {
      psp_login_go_right();
    } else
    if(new_pad & PSP_CTRL_LEFT) {
      psp_login_go_left();
    }
  }
 
  psp_kbd_wait_no_button();

  return 1;
}

