/*
   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.

   Based on the source code of PelDet written by Danzel A.
*/

#include <pspkernel.h>
#include <psppower.h>
#include <pspctrl.h>
#include <pspwlan.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <SDL.h>

#include "psp_global.h"
#include "psp_sdl.h"
#include "psp_menu.h"
#include "psp_danzeff.h"
#include "psp_fmgr.h"
#include "psp_ssh.h"

#include "psp_vt100.h"

# define VT100_MAX_LINE       400
# define VT100_MAX_WIDTH       80

typedef unsigned char bool;

# define false      0
# define true       1

#define VT100_DEFAULT_TOP_Y     0
#define VT100_DEFAULT_BOTTOM_Y  (VT100_SCREEN_HEIGHT-1)

# define COLOR_BLACK       0
# define COLOR_RED         1
# define COLOR_GREEN       2
# define COLOR_YELLOW      3
# define COLOR_BLUE        4
# define COLOR_MAGENTA     5
# define COLOR_CYAN        6
# define COLOR_WHITE       7
# define VT100_MAX_COLOR   8

# define BRIGHTNESS_NORMAL  0
# define BRIGHTNESS_BRIGHT  1
# define BRIGHTNESS_DIM     2

# define FONT_DEFAULT       0
# define FONT_LINEDRAW      1

  typedef struct vt100_elem_t {
     u8    m_char;
     u8    m_reverse;
     u8    m_brightness;
     u8    m_underlined;
     u8    m_blink;
     u8    m_font;
     u8    bg_color;
     u8    fg_color;
  } vt100_elem_t;

# define VT100_SCREEN_TOP_Y  (VT100_MAX_LINE - VT100_SCREEN_HEIGHT)

/* vt100 screen */
static vt100_elem_t vt100_screen[VT100_MAX_LINE][VT100_MAX_WIDTH];
static int          vt100_colors[VT100_MAX_COLOR][3]; /* Bright */

static int  vt100_display_from_y = 0; 

static bool wontEcho = false;
static bool cygwinHacksMode = false;  //if we detect connecting to cygwin this is set
//if this is set then backspace is sent of \b instead of \177. other telnet clients dont need to do this but i do :(

//colors and attributes of text
static int  loc_curr_fg_color = COLOR_WHITE;
static int  loc_curr_bg_color = COLOR_BLACK;
static bool loc_curr_reverse = false;
static int  loc_curr_brightness = BRIGHTNESS_NORMAL;
static int  loc_curr_underlined = 0;
static int  loc_curr_blink = 0;

static int  loc_visual_bell = 0;

# define BLINK_MAX_COUNTER     20
static int  loc_blink_counter = 0;

//what the fonts are set as and what one we are currently using
static bool G0_font = FONT_DEFAULT;
static bool G1_font = FONT_LINEDRAW;
static bool using_gX = 0;

//saved ones (from an esc sequence)
static int  loc_saved_fg_color;
static int  loc_saved_bg_color;
static bool loc_saved_reverse;
static int  loc_saved_brightness;
static int  loc_saved_underlined;
static int  loc_saved_blink;

static bool saved_G0_font;
static bool saved_G1_font;
static bool saved_using_gX;

static int loc_curr_x;
static int loc_curr_y;
static int loc_saved_x;
static int loc_saved_y;
static u8  loc_curr_font;

static int loc_top_y    = VT100_DEFAULT_TOP_Y;
static int loc_bottom_y = VT100_DEFAULT_BOTTOM_Y;

static int loc_autowrap = 0;
//cache for replies to network
static char networkBuf[200];
static int  networkBufSize = 0;

static void 
networkBufAdd(char* data, int len)
{
  int a;
  for (a = 0; a < len; a++)
  {
    networkBuf[a+networkBufSize] = data[a];
  }
  networkBufSize += len;
}

//input set things
#define APPLICATION_MODE false /* for both */

#define POSITIONING_MODE true
static bool cursorMode = POSITIONING_MODE;

#define NUMERIC_MODE true
static bool keypadMode = NUMERIC_MODE;
/*
 *  ESC [ ? 1 h       cursor keys in applications mode
 *  ESC [ ? 1 l       cursor keys in cursor positioning mode
 *  ESC =             keypad keys in applications mode
 *  ESC >             keypad keys in numeric mode
*/

static bool remoteEcho = false;  //if true the remote computer will echo back input, so we dont need to

static void
psp_vt100_clear_line(int y)
{
  int x;
  for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
    elem->m_char = ' ';
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink      = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
  }
}


static void
psp_vt100_shift_up()
{
  int y;
	if (loc_top_y == 0) {	//if we're scrolling off the top of the screen then scroll the back buffer
    for (y = 0; y <= (VT100_MAX_LINE-1-VT100_SCREEN_HEIGHT); y++) {
      vt100_elem_t *scan_line1 = &vt100_screen[y    ][0];
      vt100_elem_t *scan_line2 = &vt100_screen[y + 1][0];
      memcpy(scan_line1, scan_line2, sizeof(vt100_elem_t) * VT100_MAX_WIDTH);
    }
  }
	//now scroll the scroll area
  for (y = loc_top_y; y < loc_bottom_y; y++) {
    vt100_elem_t *scan_line1 = &vt100_screen[VT100_SCREEN_TOP_Y + y    ][0];
    vt100_elem_t *scan_line2 = &vt100_screen[VT100_SCREEN_TOP_Y + y + 1][0];
    memcpy(scan_line1, scan_line2, sizeof(vt100_elem_t) * VT100_MAX_WIDTH);
  }
  psp_vt100_clear_line(y);
  loc_curr_y--;
}

static void
psp_vt100_shift_down()
{
  int y;
  for (y = loc_bottom_y; y > loc_top_y; y--) {
    vt100_elem_t *scan_line1 = &vt100_screen[VT100_SCREEN_TOP_Y + y    ][0];
    vt100_elem_t *scan_line2 = &vt100_screen[VT100_SCREEN_TOP_Y + y - 1][0];
    memcpy(scan_line1, scan_line2, sizeof(vt100_elem_t) * VT100_MAX_WIDTH);
  }
  psp_vt100_clear_line(y);
  loc_curr_y++;
}


static void 
fixXandY(bool moveIfNeeded)
{
	if (loc_curr_x <                   0) loc_curr_x = 0;
	if (loc_curr_x >= VT100_SCREEN_WIDTH) loc_curr_x = VT100_SCREEN_WIDTH - 1;

	if (moveIfNeeded) {
		while (loc_curr_y < loc_top_y) {
      psp_vt100_shift_down();
		}
		while (loc_curr_y > loc_bottom_y) {
			psp_vt100_shift_up();
		}
	}
	else
	{
		if (loc_curr_y < loc_top_y   ) loc_curr_y = loc_top_y;
		if (loc_curr_y > loc_bottom_y) loc_curr_y = loc_bottom_y;
	}
}

static void 
renderGoto(int x, int y)
{
	loc_curr_x = x;
	loc_curr_y = y;
	fixXandY(false);
}

static void 
renderGoByReal(int x, int y, bool scroll)
{
	loc_curr_x += x;
	loc_curr_y += y;
	fixXandY(scroll);
}

static void
renderGoByMove(int x, int y)
{
  renderGoByReal(x, y, true);
}

static void
psp_vt100_shift_up_if_needed()
{
  if (loc_curr_y > loc_bottom_y) {
    psp_vt100_shift_up();
  }
}

static void
vt100_print_char(uchar c)
{
  int index = 0;
	//line wrap if need to
  if (c == '\t') {
    index = 8;
    c = ' ';
  }

  do {

	  if (loc_curr_x >= VT100_SCREEN_WIDTH) {
      loc_curr_x = 0;
      loc_curr_y++;
      psp_vt100_shift_up_if_needed();
	  }
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + loc_curr_y][loc_curr_x];
    elem->m_char = c;
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink      = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
    loc_curr_x++;

  } while (--index > 0);

}

static void
vt100_print_text(char *text)
{
  int index = 0;
  int length = strlen(text);

  for (index = 0; index < length; index++) {
    uchar c = text[index];
    if ((c < ' ') && (c != '\t')) {
			switch (c)
			{
			case '\r':
				loc_curr_x = 0;
				break;
			case '\n':
			case '\v':	//vertical tab
        loc_curr_y++;
				psp_vt100_shift_up_if_needed();
				break;
      case '\a': //bell
        if (PSPSSH.psp_visual_bell) {
          loc_visual_bell = 5;
        }
      break;
		  default:
        //LUDO: Don't print unprintable char 
# if 0 //LUDO: DEBUG
        fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, text[index]);
# endif
				break;
      }

    } else {
      if ((c == 0xc2) || (c == 0xc3)) {
        uchar next_c = text[index+1];
        if ((next_c >= 0x80) && (next_c <= 0xbf)) {
          c = psp_convert_utf8_to_iso_8859_1(c, next_c);
          index++;
        }
      }
      vt100_print_char(c);
    }
  }
}

//render text using current colors and text attributes
static void 
styledRender(char* text)
{
  vt100_print_text(text);
}

//flush the torender Buffer
#define flushBuffer() {\
  styledRender(torender);\
  torender[0] = '\0';\
  }

/*
  0 = normal char printing
  1 = just read escape character
  2 = Parsing from ^[
  
  3 = Just read a IAC (255), this means we are getting a WILL/WON'T/DO/DON'T
  4 = Read Will
  5 = Read Won't
  6 = Read Do
  7 = Read Don't
  8 = Reading Suboption
  
  9 = Ready for end of suboption
  
  10 = Parsing from ^#  (debug)
  11 = Parsing from ^(  (charset)
  12 = Parsing from ^)  (charset)
*/
static int loc_state = 0;

//int current_color = COLOR_WHITE;

char escSequence[200];
int escSequenceLen = 0;

//buffer of text to render, space for more than 2 screens of plain chars
char torender[4096];

void handleSuboption(char inputChr)
{
  if (inputChr == '\377')
  {
    loc_state = 9;
    return;
  }
  else
  {
    escSequence[escSequenceLen] = inputChr;
    escSequenceLen++;
    escSequence[escSequenceLen] = '\0';
    return;
  }
}

void handleSuboptionEnd()
{
  if (escSequence[0] == 0x18 && escSequence[1] == 0x01) //Send your terminal Type
  {
    //send suboption terminal type vt100
//    char sendbuf[11] = { 0xff, 0xfa, 0x18, 0x00, 0x56, 0x54, 0x31, 0x30, 0x30, 0xff, 0xf0 };  //vt100
//    char sendbuf[10] = { 0xff, 0xfa, 0x18, 0x00, 0x41, 0x4e, 0x53, 0x49, 0xff, 0xf0 }; //ansi
    char sendbuf[11] = { 0xff, 0xfa, 0x18, 0x00, 0x58, 0x54, 0x45, 0x52, 0x4d, 0xff, 0xf0 }; //xterm
    networkBufAdd(sendbuf, 11);
  } else 
  if (escSequence[0] == 0x20 && escSequence[1] == 0x01) //send your Terminal speed
  {
    char sendbuf[17] = { 0xff, 0xfa, 0x20, 0x00, 0x33, 0x38, 0x34, 0x30, 0x30, 0x2c, 0x33, 0x38, 0x34, 0x30, 0x30, 0xff, 0xf0 }; // 38400,38400 (could use something else)
    networkBufAdd(sendbuf, 17);
  } else
  if (escSequence[0] == 0x22 && escSequence[1] == 0x01 && escSequence[2] == 0x13)  //linemode 0x13
  {
    char sendbuf[10] = { 0xff, 0xfa, 0x22, 0x01, 0x17, 0xff, 0xf0 }; //linemode 0x17
    networkBufAdd(sendbuf, 10);
    cygwinHacksMode = true;   //This seems like a good enough place to set it...
  } else
  if (escSequence[0] == 0x27 && escSequence[1] == 0x01) // new environment option (1)
  {
    char sendbuf[6] = { 0xff, 0xfa, 0x27, 0x00, 0xff, 0xf0 };
    
    networkBufAdd(sendbuf, 6);
  }
  //TODO HANDLE OTHERS...
}

void handleWill(char inputChr)
{
  switch(inputChr)
  {
  case 0x01: //Will ECHO
    {
      remoteEcho = true;
      
      char Msg[3] = { 255, 253, 1 };  //Do Echo
      networkBufAdd(Msg, 3);
      break;
    }
  case 0x03: //Will Suppress Go Ahead
    {
      char Msg[3] = { 0xff, 0xfd, 0x03 };  //Do Suppress go Ahead
      networkBufAdd(Msg, 3);
      break;
    }
# if 0 //LUDO: DEBUG
  default :
        fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
  break;
# endif
  }
}

void handleWont(char inputChr)
{
  switch(inputChr)
  {
# if 0 //LUDO: DEBUG
  default :
        fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
  break;
# endif
  }
}

void handleDo(char inputChr)
{
  switch(inputChr)
  {
  //Things we should do (but dont yet)
  case 0x18: //Terminal Type
  break;  //i dont think we do anything here, only on a 'plz send term type' msg should we do anything?
  
  //Things we need to ignore
  case 0x20: //Terminal Speed
  case 39: //New Environment Option
  break;
  
  //Things we dont do and never will
  case 35: //X Display Location
  case 36: //Do environment Option
    {
      char wontMsg[3];    //wont <what you said>
      wontMsg[0] = 255;
      wontMsg[1] = 252;
      wontMsg[2] = inputChr;
      networkBufAdd(wontMsg, 3);
      break;
    }
  
  case 0x01: //do echo
    {
# if 0 //LUDO: ??
      renderMode(RENDER_FAST);//change render mode here to let MUD mode work right (HACK)
# endif
      wontEcho = true;
# if 0 //LUDO: ??
      renderDrawCurrentPos(true);
# endif
      char wontMsg[4];    //Wont echo
      wontMsg[0] = 255;
      wontMsg[1] = 252;
      wontMsg[2] = 1;
      networkBufAdd(wontMsg, 3);
      break;
    }
  case 0x1f: //do Negotiate about window size
    {
      char gogo[9];
      gogo[0] = 0xff;
      gogo[1] = 0xfa;
      gogo[2] = 0x1f;
      gogo[3] = 0x00;
      gogo[4] = VT100_SCREEN_HEIGHT;
      gogo[5] = 0x00;
      gogo[6] = VT100_SCREEN_WIDTH;
      gogo[7] = 0xff;
      gogo[8] = 0xf0;
      networkBufAdd(gogo, 9);
      break;
    }
  case 0x00: //do binary transmission
    {
      char willMsg[3] = { 0xff, 0xfb, 0x00 };
      networkBufAdd(willMsg, 3); //will Binary Transmission
      break;
    }
  case 0x22: //Line Mode
    {
      char lineMsg[54] = {                                              0xff, 0xfa, 0x22, 0x03, 0x01,   //05
      0x00, 0x00, 0x03, 0x62, 0x03, 0x04, 0x02, 0x0f, 0x05, 0x00, 0x00, 0x07, 0x62, 0x1c, 0x08, 0x02,    //16
      0x04, 0x09, 0x42, 0x1a, 0x0a, 0x02, 0x7f, 0x0b, 0x02, 0x15, 0x0c, 0x02, 0x17, 0x0d, 0x02, 0x12,    //16
      0x0e, 0x02, 0x16, 0x0f, 0x02, 0x11, 0x10, 0x02, 0x13, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0xff,    //16
      0xf0 };                                                                                            //01
      networkBufAdd(lineMsg, 54);
      break;
    }
# if 0 //LUDO: DEBUG
  default :
        fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
  break;
# endif
  }
}

void 
handleDont(char inputChr)
{
  switch(inputChr)
  {
  case 0x22: //Linemode
    {
      char wontMsg[3] = { 0xff, 0xfc, 0x22 };  //wont linemode
      networkBufAdd(wontMsg, 3);
      break;
    }
# if 0 //LUDO: DEBUG
  default :
        fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
  break;
# endif
  }
}

static void 
setDisplayAttribute(int attr)
{
  switch(attr)
  {
  case 0:     //Reset all attributes
    loc_curr_fg_color = COLOR_WHITE;
    loc_curr_bg_color = COLOR_BLACK;
    loc_curr_reverse = false;
    loc_curr_brightness = BRIGHTNESS_NORMAL;
    loc_curr_underlined = 0;
    loc_curr_blink = 0;
    break;
  case 1:     //Bright
    loc_curr_brightness = BRIGHTNESS_BRIGHT;
    break;
  case 2:     //Dim
    loc_curr_brightness = BRIGHTNESS_DIM;
    break;
  case 4:     //Underlined  
    loc_curr_underlined = 1;
    break;
  case 5:     //Blink
    loc_curr_blink = 1;
    break;
  case 8:     //Hidden
    break;
  case 7:     //Reverse
    loc_curr_reverse = true;
    break;
  
//    Foreground Colours
  case 30:    //Black
    loc_curr_fg_color = COLOR_BLACK;
    break;
  case 31:    //Red
    loc_curr_fg_color = COLOR_RED;
    break;
  case 32:    //Green
    loc_curr_fg_color = COLOR_GREEN;
    break;
  case 33:    //Yellow
    loc_curr_fg_color = COLOR_YELLOW;
    break;
  case 34:    //Blue
    loc_curr_fg_color = COLOR_BLUE;
    break;
  case 35:    //Magenta
    loc_curr_fg_color = COLOR_MAGENTA;
    break;
  case 36:    //Cyan
    loc_curr_fg_color = COLOR_CYAN;
    break;
  case 37:    //White
    loc_curr_fg_color = COLOR_WHITE;
    break;
  
//    Background Colours
  case 40:    //Black
    loc_curr_bg_color = COLOR_BLACK;
    break;
  case 41:    //Red
    loc_curr_bg_color = COLOR_RED;
    break;
  case 42:    //Green
    loc_curr_bg_color = COLOR_GREEN;
    break;
  case 43:    //Yellow
    loc_curr_bg_color = COLOR_YELLOW;
    break;
  case 44:    //Blue
    loc_curr_bg_color = COLOR_BLUE;
    break;
  case 45:    //Magenta
    loc_curr_bg_color = COLOR_MAGENTA;
    break;
  case 46:    //Cyan
    loc_curr_bg_color = COLOR_CYAN;
    break;
  case 47:    //White
    loc_curr_bg_color = COLOR_WHITE;
    break;
# if 0 //LUDO: DEBUG
  default :
        fprintf(stdout, "%s %d: skip attr %x\n", __FILE__, __LINE__, attr);
  break;
# endif
  }
}

static void
renderGoBy(int x, int y)
{
  renderGoByReal(x, y, false);
}

static void
psp_vt100_clear_screen(u8 c)
{
  int x;
  int y;
  for (y = 0; y < VT100_SCREEN_HEIGHT; y++) {
    for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
      vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
      elem->m_char = c;
      elem->m_font = loc_curr_font;
      elem->m_reverse = loc_curr_reverse;
      elem->m_brightness = loc_curr_brightness;
      elem->m_underlined = loc_curr_underlined;
      elem->m_blink      = loc_curr_blink;
      elem->bg_color = loc_curr_bg_color;
      elem->fg_color = loc_curr_fg_color;
    }
  }
# if 0 //LUDO: /* Needed ? */
  loc_curr_x = 0;
  loc_curr_y = 0;
# endif
}

static void
psp_vt100_clear_line_to_bottom()
{
  int x;
  int y;
  y = loc_curr_y;
  while (y < VT100_SCREEN_HEIGHT) {
    psp_vt100_clear_line(y);
    y++;
  }
}

static void
psp_vt100_clear_to_sol()
{
  int x;
  int y;
  x = loc_curr_x;
  y = loc_curr_y;
  while (x >= 0) {
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
    elem->m_char = ' ';
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
    x--;
  }
}

static void
psp_vt100_clear_to_eol()
{
  int x;
  int y;
  x = loc_curr_x;
  y = loc_curr_y;
  while (x < VT100_SCREEN_WIDTH) {
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
    elem->m_char = ' ';
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
    x++;
  }
}

static void
psp_vt100_clear_line_to_top()
{
  int x;
  int y;
  y = loc_curr_y;
  while (y >= 0) {
    psp_vt100_clear_line(y);
    y--;
  }
}

static void 
renderSetRegion(int start, int end)
{
	//Set the current render area
  if (start >= 0) {
	  loc_top_y = start;
  } else {
    start = loc_top_y;
  }
  if ((end > start) && ((end - start) < VT100_SCREEN_WIDTH)) {
	  loc_bottom_y = end;
  } else {
    loc_bottom_y = VT100_DEFAULT_BOTTOM_Y - loc_top_y;
  }
	loc_curr_x = 0;
	loc_curr_y = start;
}

void 
handleEscSequence(char inputChr)
{
  switch(inputChr)
  {
  case 'H':  //goto possition
  case 'f':
    {
      char* ret;
      int x = 0;
      int y = 0;
      
      //sscanf(escSequence, "%d ; %d", &y, &x);
      y = strtol(escSequence, &ret, 10);
      if (ret != escSequence && *ret != '\0') //read something
      {
        ret++;  //hopefully we skip a ';'
        x = strtol(ret, NULL, 10);
      }
      else
        y = 0;
      
      //if they are numbers, we need to -- them
      if (x >= 1) x--;
      if (y >= 1) y--;
      
      flushBuffer();
      renderGoto(x,y);
      break;
    }
  case 'A':  //Move up by X
    {
      int moves = 1;
      moves = atoi(escSequence);
      if (moves == 0) moves = 1;
      flushBuffer();
      renderGoBy(0, -moves);
      break;
    }
  case 'B':  //Move down by X
    {
      int moves = 1;
      moves = atoi(escSequence);
      if (moves == 0) moves = 1;
      flushBuffer();
      renderGoBy(0, moves);
      break;
    }
  case 'C':  //Move Right by X
    {
      int moves = 1;
      moves = atoi(escSequence);
      if (moves == 0) moves = 1;
      flushBuffer();
      renderGoBy(moves, 0);
      break;
    }
  case 'D':  //Move left by X
    {
      int moves = 1;
      moves = atoi(escSequence);
      if (moves == 0) moves = 1;
      flushBuffer();
      renderGoBy(-moves, 0);
      break;
    }
  case 's':  //Save position
    flushBuffer();
    loc_saved_x = loc_curr_x;
    loc_saved_y = loc_curr_y;
    break;
  case 'u':  //Load Position
    flushBuffer();
    loc_curr_x = loc_saved_x;
    loc_curr_y = loc_saved_y;
    break;
    
  case 'K':
    switch(escSequence[0])
    {
    case '\0':
    case '0':  //Erase to end of line
      flushBuffer();
      psp_vt100_clear_to_eol();
      break;
    case '1':  //Erase to start of line
      flushBuffer();
      psp_vt100_clear_to_sol();
      break;
    case '2':  //Erase current line
      flushBuffer();
      psp_vt100_clear_line(loc_curr_y);
      break;
# if 0 //LUDO: DEBUG
    default :
        fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[0]);
    break;
# endif
    }
    break;
  case 'J':
    switch(escSequence[0])
    {
    case '\0':
    case '0':  //Erase current line to bottom
      flushBuffer();
      psp_vt100_clear_to_eol();
      renderGoBy(0,1);
      psp_vt100_clear_line_to_bottom();
      renderGoBy(0,-1);
      break;
    case '1':  //Erase current line to top
      flushBuffer();
      psp_vt100_clear_to_sol();
      renderGoBy(0,-1);
      psp_vt100_clear_line_to_top();
      renderGoBy(0,1);
      break;
    case '2':  //Erase screen
      flushBuffer();
      psp_vt100_clear_screen(' ');
      break;
# if 0 //LUDO: DEBUG
    default :
        fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[0]);
      break;
# endif
    }
    break;
  case 'm':  //Display Attributes
    {
      flushBuffer();
      int attr = 0;
      int a;
      bool setone = true;  //(true so we process a ^[m as a ^[0m, which it is)
      for (a=0; a < escSequenceLen; a++)
      {
        if (escSequence[a] >= '0' && escSequence[a] <= '9')
        {
          attr *= 10;
          attr += (escSequence[a]-'0');
          setone = true;
        }
        else
        {
          if (setone)
            setDisplayAttribute(attr);
          setone = false;
          attr = 0;
        }
      }
      if (setone)
        setDisplayAttribute(attr);
      break;
    }
  case 'h':  //Some setup things
    {
      if (escSequenceLen == 2)
        switch(escSequence[1])
        {
          case '1':  //cursor keys in applications mode
            cursorMode = APPLICATION_MODE;
            break;
          case '5':  //black chars on white BG (TODO should this change bold etc)
            loc_curr_fg_color = COLOR_BLACK;
            loc_curr_bg_color   = COLOR_WHITE;
            break;
          case '6':  //turn on region - origin mode  (TODO i dont actually know what this is ment to do, so ill fullscreen it)
            flushBuffer();
            renderSetRegion(VT100_DEFAULT_TOP_Y, VT100_DEFAULT_BOTTOM_Y);
            break;
          case '7':
            loc_autowrap = 1;
            break;
# if 0 //LUDO: DEBUG
          default :
            fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[1]);
          break;
# endif
        }
      break;
    }
   case 'l':  //Some un-setup things
    {
      if (escSequenceLen == 2)
        switch(escSequence[1])
        {
          case '1':  //cursor keys in cursor positioning mode
            cursorMode = POSITIONING_MODE;
            break;
          case '5':  //white chars on black BG (TODO should this change bold etc)
            loc_curr_fg_color = COLOR_WHITE;
            loc_curr_bg_color   = COLOR_BLACK;
            break;
          case '6':  //turn on region - full screen mode
            flushBuffer();
            renderSetRegion(VT100_DEFAULT_TOP_Y, VT100_DEFAULT_BOTTOM_Y);
            break;
          case '7': // reset autowrap
            loc_autowrap = 0;
            break;
# if 0 //LUDO: DEBUG
          default :
/*
setcol DECCOLM        Set number of columns to 132           ^[[?3h
setsmooth DECSCLM     Set smooth scrolling                   ^[[?4h
setrevscrn DECSCNM    Set reverse video on screen            ^[[?5h
setorgrel DECOM       Set origin to relative                 ^[[?6h
setwrap DECAWM        Set auto-wrap mode                     ^[[?7h
setrep DECARM         Set auto-repeat mode                   ^[[?8h
setinter DECINLM      Set interlacing mode                   ^[[?9h
*/
            fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[1]);
          break;
# endif
        }
      break;
    }
  case 'c':  //tell me your terminal type / status
    {
//     *  ESC [ c           request to identify terminal type
//     *  ESC [ 0 c         request to identify terminal type
//     *  ESC Z             request to identify terminal type
      //Always assume its the same thing, send back a report saying we are vt100
      char *tosend = "\033[?1;0c";
      psp_ssh_put_data_to_ssh(tosend, 7);
      break;
    }
  case 'r':  //set a scroll region
    {
      //   "12;13"
      int start = VT100_DEFAULT_TOP_Y +1;
      int end = VT100_DEFAULT_BOTTOM_Y +1;
//      sscanf(escSequence, "%d ; %d", &start, &end);
      char* ret;
      start = strtol(escSequence, &ret, 10);
      if (ret != escSequence && *ret != '\0') //read something
      {
        ret++;  //hopefully we skip a ';'
        end = strtol(ret, NULL, 10);
      }
      else  //if we didnt read anything, we need to reset start to its original value
      {
        start = VT100_DEFAULT_TOP_Y +1;
      }
      
      start--; end--;
      
      flushBuffer();
      renderSetRegion(start, end);
      break;
    }
# if 0 //LUDO:
  case 'n' :
//     *  ESC [ 6 n   Cursor position request
    {
      char buffer[32];
      sprintf(buffer, "\033[%d;%dR", loc_curr_y + 1, loc_curr_x + 1);
      int l = strlen(buffer);
      psp_ssh_put_data_to_ssh(buffer, l);
      break;
    }
# endif
/*    HACK TODO - part of 'ansi' terminal, BitchX borks hard when on ansi and this is part of an almost fix.
          however! xterm and putty bork with BitchX in ansi mode, so we just wont go there ;)
  case 'S':  //cursor down - (TODO) at bottom of region, scroll up
    loc_state = 0;
    flushBuffer();
    renderScroll(1);
//    renderGoByMove(0, 1);
    break;*/
  }
}

static void
renderSetFont(int new_font)
{
  loc_curr_font = new_font;
}

void 
psp_vt100_process(char* inputStr, int length)
{
# if 0 //LUDO: DEBUG
  {
    int index;
    for (index = 0; index < length; index++) {
      fprintf(stdout, "[%d] %x '%c'\n", index, 
              inputStr[index], (inputStr[index] >= ' ') ? inputStr[index] : 'X');
    }
  }
# endif
  torender[0] = '\0';
  
  int a;
  for (a = 0; a < length; a++)
  {
    switch (inputStr[a])  //need to handle esc chars first, so they arent picked up in an esc seq
    {
    case '\n':
    case '\r':
    case '\v':  //(vertical tab)
      {
        strncat(torender, &inputStr[a], 1);
        break;
      }
    case '\b':
      //flush
      flushBuffer();
      //move back 1
      renderGoBy(-1, 0);
      break;
//    case '\000':  //According to a capture from bitchx in ansi mode, this may be newline too :|
      //Not so sure anymore, might remove this later...
//      strncat(torender, "\n", 1);
      break;
    case 14:  // 'shift out'  Switch to alt font G1
      flushBuffer();
      using_gX = 1;
      renderSetFont(G1_font);
      break;
    case 15:  // 'shift in' Switch to normal font G0
      flushBuffer();
      using_gX = 0;
      renderSetFont(G0_font);
      break;
      
    default:
      switch (loc_state)
      {
      case 0:
        switch (inputStr[a])
        {
        case '\033': //Escape Character
          loc_state = 1;
          break;
        case '\377': // 255 (before a will/wont)
          loc_state = 3;
          break;
        default:
          strncat(torender, &inputStr[a], 1);
        }
        break;
      case 1:
        switch (inputStr[a])
        {
        case '[':
          loc_state = 2;
          break;
        case '#':
          loc_state = 10;
          break;
        case '7':  //save cursor pos
          loc_state = 0;
          flushBuffer();
          loc_saved_x = loc_curr_x;
          loc_saved_y = loc_curr_y;
          //also save some extras
          loc_saved_fg_color   = loc_curr_fg_color;
          loc_saved_bg_color   = loc_curr_bg_color;
          loc_saved_reverse    = loc_curr_reverse;
          loc_saved_brightness = loc_curr_brightness;
          loc_saved_underlined = loc_curr_underlined;
          loc_saved_blink      = loc_curr_blink;
          saved_G0_font = G0_font;
          saved_G1_font = G1_font;
          saved_using_gX = using_gX;
          break;
        case '8': //restore cursor pos
          loc_state = 0;
          flushBuffer();
          //send load msg
          loc_curr_x = loc_saved_x;
          loc_curr_y = loc_saved_y;
          //also load some extras
          loc_curr_fg_color = loc_saved_fg_color;
          loc_curr_bg_color = loc_saved_bg_color;
          loc_curr_reverse = loc_saved_reverse;
          loc_curr_brightness = loc_saved_brightness;
          loc_curr_underlined = loc_saved_underlined;
          loc_curr_blink      = loc_saved_blink;
          G0_font     = saved_G0_font;
          G1_font     = saved_G1_font;
          using_gX    = saved_using_gX;
          
          if (using_gX == 0) {
            loc_curr_font = G0_font;
          } else {
            loc_curr_font = G1_font;
          }
          break;
        case 'E':  //New line
          loc_state = 0;
          strncat(torender, "\r\n", 2);
          break;
        case 'D':  //cursor down - (TODO) at bottom of region, scroll up
          loc_state = 0;
          flushBuffer();
          renderGoByMove(0, 1);
          break;
        case 'M':  //cursor up - (TODO) at top of region, scroll down
          loc_state = 0;
          flushBuffer();
          renderGoByMove(0, -1);
          break;
          
        case '>':  //keypad keys in numeric mode *shrug*
        case '=':  //keypad keys in applications mode *shrug*
          loc_state = 0;
          break;
        case '(':
          loc_state = 11;
          break;
        case ')':
          loc_state = 12;
          break;
# if 0 //LUDO: DEBUG
        default :
          fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputStr[a]);
        break;
# endif
        }
        break;
      case 2:
        //End of Esc Sequence
        if ((inputStr[a] >= 'a' && inputStr[a] <= 'z') || (inputStr[a] >= 'A' && inputStr[a] <= 'Z'))
        {
          escSequence[escSequenceLen] = '\0';
          loc_state = 0;
          
          handleEscSequence(inputStr[a]);
          
          //Processed
          escSequenceLen = 0;
        }
        else
        {
          escSequence[escSequenceLen] = inputStr[a];
          escSequenceLen++;
        }
        break;
      case 3:
        switch(inputStr[a])
        {
        case '\373': //WILL
          loc_state = 4;
          break;
        case '\374': //WON'T
          loc_state = 5;
          break;
        case '\375': //DO
          loc_state = 6;
          break;
        case '\376': //DON'T
          loc_state = 7;
          break;
        case '\372': //suboption begin
          loc_state = 8;
          escSequence[0] = '\0';
          escSequenceLen = 0;
          break;
# if 0 //LUDO: DEBUG
        default :
          fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputStr[a]);
          break;
# endif
        }
        break;
      case 4: //WILL
        loc_state = 0;
        handleWill(inputStr[a]);
        break;
      case 5: //WON'T
        loc_state = 0;
        handleWont(inputStr[a]);
        break;
      case 6: //DO
        loc_state = 0; //Assume we can reset the loc_state.
        handleDo(inputStr[a]);
        break;
      case 7: //DON'T
        loc_state = 0;
        handleDont(inputStr[a]);
        break;
      case 8: //In Suboption
        handleSuboption(inputStr[a]);
        break;
      case 9: //Suboption escaped thing
        if (inputStr[a] == '\360') //end char
        {
          loc_state = 0;
          handleSuboptionEnd();
        }
        break;
      case 10:
        {
          int q;
          switch(inputStr[a])
          {
          case '8':  //Fill screen with E OMGOES
            flushBuffer();
            psp_vt100_clear_screen('E');
            renderGoto(0,0);
            break;
# if 0 //LUDO: DEBUG
          default :
            fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputStr[a]);
            break;
# endif
          }
          loc_state = 0;
          break;
        }
      case 11: // ^(X
        loc_state = 0;
        if (inputStr[a] == 'A' || inputStr[a] == 'B')
          G0_font = FONT_DEFAULT;
        else if (inputStr[a] == '0')
          G0_font = FONT_LINEDRAW;
        
        if (using_gX == 0) {
          flushBuffer();
          loc_curr_font = G0_font;
        }
        break;
      case 12: // ^)X
        loc_state = 0;
        if (inputStr[a] == 'A' || inputStr[a] == 'B')
          G1_font = FONT_DEFAULT;
        else if (inputStr[a] == '0')
          G1_font = FONT_LINEDRAW;
        
        if (using_gX == 1)
        {
          flushBuffer();
          loc_curr_font = G1_font;
        }
        break;
        
        
        //Else who knows what happened, assume its a fuckup and do nothing....
      }
      
    }
    //The great big debug hack lol
/*    {
      char aaa[10];
      sprintf(aaa, "%d(%d) ", inputStr[a], loc_state);
      strcat(torender, aaa);
    }  */
  }
  
  //FLUSH
  flushBuffer();

  if (networkBufSize != 0) {
    psp_ssh_put_data_to_ssh(networkBuf, networkBufSize);
    networkBufSize = 0;
  }
}

void
psp_vt100_screen_scroll_up()
{
  vt100_display_from_y -= VT100_SCREEN_HEIGHT / 2;
  if (vt100_display_from_y < 0) vt100_display_from_y = 0;
}

void
psp_vt100_screen_scroll_down()
{
  vt100_display_from_y += VT100_SCREEN_HEIGHT / 2;
  if (vt100_display_from_y > VT100_SCREEN_TOP_Y) vt100_display_from_y = VT100_SCREEN_TOP_Y;
}

void
psp_vt100_display_screen()
{
# if 0 //LUDO: FOR_DEBUG
  {
    char buffer[32];
    sprintf(buffer, "(%d %d)", loc_curr_x, loc_curr_y);
    int color = psp_sdl_rgb(0xff, 0xff, 0xff);
    psp_sdl_back_print(40, 6, buffer, color);
  }
# endif

  psp_sdl_select_font_6x10();

  int line = 0;
  int y_pix = 0;

  int first_line = vt100_display_from_y;
  int last_line  = vt100_display_from_y + VT100_SCREEN_HEIGHT;

  if (last_line >= VT100_MAX_LINE) {
    first_line = VT100_SCREEN_TOP_Y;
    last_line  = VT100_SCREEN_TOP_Y + VT100_SCREEN_HEIGHT - 1;
  }

  int abs_curr_y = loc_curr_y + VT100_SCREEN_TOP_Y;
  for (line = first_line; line <= last_line; line++) {
    vt100_elem_t *scan_line = &vt100_screen[line][0];
    int x_pix = 0;
    int x;
    for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
      int bright = scan_line[x].m_brightness;
      int blink  = scan_line[x].m_blink;
      int underlined = scan_line[x].m_underlined;
      int fg_color = vt100_colors[scan_line[x].fg_color][bright];
      int bg_color = vt100_colors[scan_line[x].bg_color][bright];
      if (scan_line[x].m_reverse) {
        int swap_color = fg_color;
        fg_color = bg_color;
        bg_color = swap_color;
      }
      if (loc_visual_bell) {
        int swap_color = fg_color;
        fg_color = bg_color;
        bg_color = swap_color;
      }
      uchar m_char = scan_line[x].m_char;
      if (loc_visual_bell) {
        if (fg_color == bg_color) {
          fg_color = vt100_colors[loc_curr_bg_color][bright];
          bg_color = vt100_colors[loc_curr_fg_color][bright];
        }
        if (m_char < ' ') m_char = ' ';
      }
      if (blink && (loc_blink_counter < (BLINK_MAX_COUNTER/2))) {
        fg_color = bg_color;
      }
      psp_sdl_put_char(x_pix, y_pix, fg_color, bg_color, m_char, 1, 1);

      /* Display cursor */
      if ((abs_curr_y == line) && (x == loc_curr_x)) {
        psp_sdl_put_char(x_pix, y_pix, vt100_colors[loc_curr_fg_color][1], 0, '_', 1, 0);
      } else
      if (underlined) {
        psp_sdl_put_underline(x_pix, y_pix, fg_color);
      }
      x_pix += psp_font_width;
    }
    y_pix += psp_font_height;
  }
  if (loc_visual_bell > 0) loc_visual_bell--;
  loc_blink_counter = (loc_blink_counter + 1) % BLINK_MAX_COUNTER;

  psp_sdl_select_font_8x8();
}

void
psp_vt100_init()
{
  int bright;
  int c;
  for (bright = 0; bright < 3; bright++) {
    if (bright == 0) c = 0xaF;
    else
    if (bright == 1) c = 0xFF;
    else             c = 0xcF;

    vt100_colors[COLOR_BLACK  ][bright] = psp_sdl_rgb(0x00, 0x00, 0x00);
    vt100_colors[COLOR_RED    ][bright] = psp_sdl_rgb(c   , 0x00, 0x00);
    vt100_colors[COLOR_GREEN  ][bright] = psp_sdl_rgb(0x00, c   , 0x00);
    vt100_colors[COLOR_BLUE   ][bright] = psp_sdl_rgb(0x00, 0x00, c   );
    vt100_colors[COLOR_YELLOW ][bright] = psp_sdl_rgb(c   , c   , 0x00);
    vt100_colors[COLOR_MAGENTA][bright] = psp_sdl_rgb(c   , c   , 0x00);
    vt100_colors[COLOR_CYAN   ][bright] = psp_sdl_rgb(0x00, c   , c   );
    vt100_colors[COLOR_WHITE  ][bright] = psp_sdl_rgb(c   , c   , c   );
  }

  loc_saved_x = loc_curr_x = 0;
  loc_saved_y = loc_curr_y = 0;

  loc_curr_font = 0;

  vt100_display_from_y = VT100_SCREEN_TOP_Y;
  memset(vt100_screen, 0, sizeof(vt100_screen));
}

void
psp_vt100_save_log(void)
{
  FILE* File;
  char  TmpFileName[MAX_PATH];
  int   y = 0;
  int   x = 0;
  uchar v = 0;

  sprintf(TmpFileName,"%s/log/log_%d.txt", PSPSSH.psp_homedir, PSPSSH.psp_log_id++);
  if (PSPSSH.psp_log_id >= 10) PSPSSH.psp_log_id = 0;
  File = fopen(TmpFileName, "w");
  if (File) {
    for (y = 0; y < VT100_MAX_LINE; y++) {
      for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
        vt100_elem_t *elem = &vt100_screen[y][x];
        if (elem->m_char) break;
      }
      if (x != VT100_SCREEN_WIDTH) break;
    }
    while (y < VT100_MAX_LINE) {
      for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
        v = vt100_screen[y][x].m_char;
        if (v < ' ') v = ' ';
        fputc(v, File);
      }
      fputc('\n', File);
      y++;
    }
    fclose(File);
  }
}

