/*
   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 <pspwlan.h>
#include <pspkernel.h>
#include <psppower.h>
#include <pspctrl.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_brightness;
     u8    m_reverse;
     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];

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;

//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 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;

//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->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_text(char *text)
{
  int index = 0;
  int length = strlen(text);

  for (index = 0; index < length; index++) {
    if (text[index] < ' ') {
			switch (text[index])
			{
			case '\r':
				loc_curr_x = 0;
				break;
			case '\n':
			case '\v':	//vertical tab
        loc_curr_y++;
				psp_vt100_shift_up_if_needed();
				break;
		  default:
        //LUDO: Don't print unprintable char 
				break;
     }
   } else {
			//line wrap if need to
			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 = text[index];
      elem->m_font = loc_curr_font;
      elem->m_reverse = loc_curr_reverse;
      elem->m_brightness = loc_curr_brightness;
      elem->bg_color = loc_curr_bg_color;
      elem->fg_color = loc_curr_fg_color;
      loc_curr_x++;
		}
  }
}

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


# if 0 //LUDO:
/* A thread which implements the telnet protocol */
int telnetProtocol(SceSize args, void *argp)
{
  ProtocolMsg* data;
  int error;
  
  //send initial hello type message
  char testset[100];
  sprintf(testset, "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c", 
  0xff, 0xfd, 0x03,  //Do Suppress Go Ahead
  0xff, 0xfb, 0x18,  //Will Terminal Type
  0xff, 0xfb, 0x1f,  //Will Negotiate About Window Size
  0xff, 0xfb, 0x20,  //Will Terminal Speed
  0xff, 0xfb, 0x21,  //Will Remote Flow Control  (sorta HACK, we just ignore future messages about it)
  0xff, 0xfb, 0x22,  //Will Line Mode      (sorta HACK, we only can really handle talking to cygwin and others that behave the same)
  0xff, 0xfb, 0x27,  //Will New Environment Option (almost HACK)
  0xff, 0xfd, 0x05  //Do Status
  );
  networkSend(testset);
  
  setDisplayAttribute(0);//reset display
//  renderMode(RENDER_FAST);
  
  while (1)
  {
  
    error = sceKernelReceiveMbx(protocolMessagebox, (void*)&data, NULL);
    if (error < 0)
    {
      //OH NO ERROR :(
      //tell renderer an error i guess....
      renderMain("TELNET IS DIED OHES NOES", COLOR_RED);
      return -1;
    }
    
    switch(data->from)
    {
    case FROM_NETWORK:
      vt100Render(data->text, data->length);
      
      if(networkBufSize != 0)
      {
        psp_ssh_put_data_to_ssh(networkBuf, networkBufSize);
        networkBufSize = 0;
      }
      break;
    case FROM_USER:
      {
        if (!wontEcho)
        {
          char tosend[100];
          sprintf(tosend, "%s\r\n", data->text); 
          networkSend(tosend);
          styledRender(tosend);
        }
        else
        {
          if (data->text[0] == '\n' || data->text[0] == '\r')
          {
            if (cygwinHacksMode)  //okay so this isnt actually a cygwin hack as it has been negotiated from linemode, but it is okay for now
            {
              psp_ssh_put_data_to_ssh("\r", 1);  //  (CYGWIN HACK)
            }
            else
            {
              psp_ssh_put_data_to_ssh("\r\0", 2);
            }
            
            if (!remoteEcho)
              styledRender("\r\n");
          }
          else
          {
            //Need to do some replacement of charcodes when sending, not sure which ;)
            switch(data->text[0])
            {
              case '\b':
                if (cygwinHacksMode)
                {
                  psp_ssh_put_data_to_ssh("\b", 1);
                }
                else
                {
                  psp_ssh_put_data_to_ssh("\177", 1);
                }
                if (!remoteEcho)
                {
                  char* forscreen = "\b \b";
                  styledRender(forscreen);
                }
                break;
              default:
                psp_ssh_put_data_to_ssh(data->text, 1);
                if (!remoteEcho)
                {
                  char forscreen[2];
                  forscreen[0] = data->text[0];
                  forscreen[1] = '\0';
                  styledRender(forscreen);
                }
            }
            
          }
        }
        break;
      }
    case FROM_USER_CODE:
      {
        char* code = "";
        int len = 0;
        
        switch(data->text[0])
        {
        case 33:  //page up
          code = "\033[5~";
          len = 4;
          break;
        case 34:  //page down
          code = "\033[6~";
          len = 4;
          break;
        case 35:  //end
          code = "\033[F";
          len = 3;
          break;
        case 36:  //home
          code = "\033[H";
          len = 3;
          break;
        case 37:  //left
          if (cursorMode == POSITIONING_MODE)
            code = "\033[D";
          else
            code = "\033OD";
          len = 3;
          break;
        case 38:  //up
          if (cursorMode == POSITIONING_MODE)
            code = "\033[A";
          else
            code = "\033OA";
          len = 3;
          break;
        case 39:  //right
          if (cursorMode == POSITIONING_MODE)
            code = "\033[C";
          else
            code = "\033OC";
          len = 3;
          break;
        case 40:  //down
          if (cursorMode == POSITIONING_MODE)
            code = "\033[B";
          else
            code = "\033OB";
          len = 3;
          break;
        case 45:  //Insert
          code = "\033[2~";
          len = 4;
          break;
        case 46:  //Delete
          code = "\033[3~";
          len = 4;
          break;
        }
        
        psp_ssh_put_data_to_ssh(code, len);
        break;
      }
    }
    free(data->text);
    free(data);
  }
  
  //Should never happen
  return 0;
}
# endif

//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;
    }
  }
}

void handleWont(char inputChr)
{
  switch(inputChr)
  {
  }
}

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;
    }
  }
}

void 
handleDont(char inputChr)
{
  switch(inputChr)
  {
  case 0x22: //Linemode
    {
      char wontMsg[3] = { 0xff, 0xfc, 0x22 };  //wont linemode
      networkBufAdd(wontMsg, 3);
      break;
    }
  }
}

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;
    break;
  case 1:     //Bright
    loc_curr_brightness = BRIGHTNESS_BRIGHT;
    break;
  case 2:     //Dim
    loc_curr_brightness = BRIGHTNESS_DIM;
    break;
/*  case 4:     //Underscore  
  case 5:     //Blink
  case 8:     //Hidden
*/
  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;
  }
}

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->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->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->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_SCREEN_HEIGHT - loc_top_y - 1;
  }
	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;
    }
    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;
    }
    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;
        }
      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;
        }
      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;
    }
/*    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)
{
  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;
          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;
          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;
    //    default:  //Unknown :(
        }
        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;
        default:
          //UNKNOWN OMGOES
          break;
        }
        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;
          }
          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 = 20;

  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 fg_color = vt100_colors[scan_line[x].fg_color];
      int bg_color = vt100_colors[scan_line[x].bg_color];
      if (scan_line[x].m_reverse) {
        int swap_color = fg_color;
        fg_color = bg_color;
        bg_color = swap_color;
      }
      /* Attribute? TO_BE_DONE ! */
      psp_sdl_put_char(x_pix, y_pix, fg_color, bg_color, scan_line[x].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], 0, '_', 1, 0);
      }
      x_pix += psp_font_width;
    }
    y_pix += psp_font_height;
  }

  psp_sdl_select_font_8x8();
}

void
psp_vt100_init()
{
  vt100_colors[COLOR_BLACK  ] = psp_sdl_rgb(0x00, 0x00, 0x00);
  vt100_colors[COLOR_RED    ] = psp_sdl_rgb(0xFF, 0x0F, 0x00);
  vt100_colors[COLOR_GREEN  ] = psp_sdl_rgb(0x00, 0xFF, 0x00);
  vt100_colors[COLOR_YELLOW ] = psp_sdl_rgb(0xFF, 0xFF, 0x00);
  vt100_colors[COLOR_BLUE   ] = psp_sdl_rgb(0x00, 0xFF, 0x00);
  vt100_colors[COLOR_MAGENTA] = psp_sdl_rgb(0xFF, 0x00, 0xFF);
  vt100_colors[COLOR_CYAN   ] = psp_sdl_rgb(0xFF, 0xFF, 0x00);
  vt100_colors[COLOR_WHITE  ] = psp_sdl_rgb(0xFF, 0xFF, 0xFF);

  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;

# if 0 //LUDO: FOR_DEBUG
  int x;
  int y;
  for (y = 0; y < VT100_MAX_LINE; y++) {
    char buffer[32];
    sprintf(buffer, "line %d", y);
    int l = strlen(buffer);
    for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
      vt100_elem_t *elem = &vt100_screen[y][x];
      if (x < l) elem->m_char = buffer[x];
      else       elem->m_char = ' ';
      elem->m_font = loc_curr_font;
      elem->m_reverse = loc_curr_reverse;
      elem->m_brightness = loc_curr_brightness;
      elem->bg_color = loc_curr_bg_color;
      elem->fg_color = loc_curr_fg_color;
    }
  }
# endif
}

