/* -*- c -*- ------------------------------------------------------------- *
 *   
 *   Copyright 2004-2005 Murali Krishnan Ganapathy - All Rights Reserved
 *
 *   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, Inc., 53 Temple Place Ste 330,
 *   Boston MA 02111-1307, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

#include <string.h>
#include <com32.h>
#include "tui.h"
#include "syslnx.h"
#include "com32io.h"
#include "scancodes.h"
#include <stdlib.h>

com32sys_t inreg,outreg; // Global register sets for use

char bkspstr[] = " \b$";
char eolstr[] = "\n$";
#define GETSTRATTR 0x07

// Reads a line of input from stdin. Replace CR with NUL byte
// password <> 0 implies not echoed on screen
// showoldvalue <> 0 implies currentvalue displayed first
// If showoldvalue <> 0 then caller responsibility to ensure that
// str is NULL terminated.
void getuserinput(char *stra, unsigned int size, unsigned int password, 
  unsigned int showoldvalue)
{
    unsigned char c,scan;
    char *p,*q; // p = current char of string, q = tmp
    char *last; // The current last char of string
    char *str; // pointer to string which is going to be allocated
    char page;
    char row,col;
    char start,end; // Cursor shape
    char fudge; // How many chars should be removed from output
    char insmode; // Are we in insert or overwrite

    page = getdisppage();
    getpos(&row,&col,page); // Get current position
    getcursorshape(&start,&end);
    insmode = 1;

    str = (char *)malloc(size+1); // Allocate memory to store user input
    memset(str,0,size+1); // Zero it out
    if (password != 0) showoldvalue = 0; // Password's never displayed

    if (showoldvalue != 0) strcpy(str,stra); // If show old value copy current value

    last = str;
    while (*last) {last++;} // Find the terminating null byte
    p = str+ strlen(str);

    if (insmode == 0)
       setcursorshape(1,7); // Block cursor
    else setcursorshape(6,7); // Normal cursor

    // Invariants: p is the current char
    // col is the corresponding column on the screen
    if (password == 0) // Not a password, print initial value
    { 
       gotoxy(row,col,page);
       csprint(str,GETSTRATTR);
    }
    while (1) { // Do forever
      c = inputc(&scan);
      if (c == '\r') break; // User hit Enter getout of loop
      if (scan == ESCAPE) // User hit escape getout and nullify string
      { *str = 0;
        break;
      }
      fudge = 0;
        // if scan code is regognized do something 
        // else if char code is recognized do something
        // else ignore
      switch(scan) {
        case HOMEKEY:
             p = str;
             break;
        case ENDKEY:
             p = last;
             break;
        case LTARROW:
             if (p > str) p--;
             break;
        case CTRLLT: 
             if (p==str) break;
             if (*p == ' ') 
                while ((p > str) && (*p == ' ')) p--;
             else {
                if (*(p-1) == ' ') {
                   p--; 
                   while ((p > str) && (*p == ' ')) p--;
                }
             }
             while ((p > str) && ((*p == ' ') || (*(p-1) != ' '))) p--;
             break;
        case RTARROW:
             if (p < last) p++;
             break;
        case CTRLRT: 
             if (*p==0) break; // At end of string
             if (*p != ' ') 
                while ((*p!=0) && (*p != ' ')) p++;
             while ((*p!=0) && ((*p == ' ') && (*(p+1) != ' '))) p++;
             if (*p==' ') p++;
             break;
        case DELETE:
             q = p;
             while (*(q+1)) {*q = *(q+1); q++; }
             if (last > str) last--;
             fudge = 1;
             break;
        case INSERT:
           insmode = 1-insmode; // Switch mode
           if (insmode == 0)
              setcursorshape(1,7); // Block cursor
           else setcursorshape(6,7); // Normal cursor
           break;

        default: // Unrecognized scan code, look at the ascii value
	  switch (c) {
	  case '\b': // Move over by one
              q=p;
	      while ( q <= last ) { *(q-1)=*q; q++;}
              if (last > str) last--;
              if (p > str) p--;
              fudge = 1;
	      break;
	  case '\x15':		/* Ctrl-U: kill input */
              fudge = last-str;
	      while ( p > str ) *p--=0;
              p = str; *p=0; last = str;
	      break;
	  default: // Handle insert and overwrite mode
	      if ((c >= ' ') && (c < 128) && 
                  ((unsigned int)(p-str) < size-1) ) {
                if (insmode == 0) { // Overwrite mode
                  if (p==last) last++;
                  *last = 0;
	          *p++ = c;
                } else {  // Insert mode
                  if (p==last) { // last char
                     last++;
                     *last=0;
                     *p++=c;
                  } else { // Non-last char
                     q=last++;
                     while (q >= p) { *q=*(q-1); q--;}
                     *p++=c;
                  }
                }
              }
              else beep();
	  }
	  break;
	}
        // Now the string has been modified, print it
        if (password == 0) {
          gotoxy(row,col,page);
          csprint(str,GETSTRATTR);
          if (fudge > 0) cprint(' ',GETSTRATTR,fudge,page);
          gotoxy(row,col+(p-str),page);
        }
      }
    *p = '\0';
    if (password == 0) csprint("\r\n",GETSTRATTR);
    setcursorshape(start,end); // Block cursor
    // If user hit ESCAPE so return without any changes
    if (scan != ESCAPE) strcpy(stra,str);
    free(str);
}

/* Print a C string (NUL-terminated) */
void cswprint(const char *str,char attr,char left)
{
    char page = getdisppage();
    char newattr=0,cha,chb;
    char row,col;
    char nr,nc;

    nr = getnumrows();
    nc = getnumcols();
    getpos(&row,&col,page);
    while ( *str ) {
      switch (*str) 
	{
	case '\b':
	  --col;
	  break;
	case '\n':
	  ++row;
          col = left;
	  break;
	case '\r':
	  //col=left;
	  break;
	case BELL: // Bell Char
	  beep();
	  break;
	case CHRELATTR: // change attribute (relatively)
	case CHABSATTR: // change attribute (absolute)
	  cha = *(str+1);
	  chb = *(str+2);
	  if ((((cha >= '0') && (cha <= '9')) || 
	       ((cha >= 'A') && (cha <= 'F'))) &&
	      (((chb >= '0') && (chb <= '9')) || 
	       ((chb >= 'A') && (chb <= 'F')))) // Next two chars are legal
	    {
	      if ((cha >= 'A') && (cha <= 'F'))
		cha = cha - 'A'+10;
	      else cha = cha - '0';
	      if ((chb >= 'A') && (chb <= 'F'))
		chb = chb - 'A'+10;
	      else chb = chb - '0';
	      newattr = (cha << 4) + chb;
	      attr = (*str == CHABSATTR ? newattr : attr ^ newattr);
	      str += 2; // Will be incremented again later
	    }
	  break;
	default:
	  putch(*str, attr, page);
	  ++col;
	}
      if (col >= nc)
	{
	  ++row;
	  col=left;
	}
      if (row > nr)
	{
	  scrollup();
	  row= nr;
	}
      gotoxy(row,col,page);
      str++;
    }
}

void clearwindow(char top, char left, char bot, char right, char page, char fillchar, char fillattr)
{
    char x;
    for (x=top; x < bot+1; x++)
    {
        gotoxy(x,left,page);
        cprint(fillchar,fillattr,right-left+1,page);
    }
}

void cls(void)
{
  unsigned char dp = getdisppage();
  gotoxy(0,0,dp);
  cprint(' ',GETSTRATTR,(1+getnumrows())*getnumcols(),dp);
}

//////////////////////////////Box Stuff

// This order of numbers must match
// the values of BOX_TOPLEFT,... in the header file

unsigned char SINSIN_CHARS[] = {218,192,191,217, //Corners
                              196,179, // Horiz and Vertical
                              195,180,194,193,197}; // Connectors & Middle

unsigned char DBLDBL_CHARS[] = {201,200,187,188,  // Corners
                              205,186, // Horiz and Vertical
                              199,182,203,202,206}; // Connectors & Middle

unsigned char SINDBL_CHARS[] =  {214,211,183,189, // Corners
                                 196,186, // Horiz & Vert
                                 199,182,210,208,215}; // Connectors & Middle

unsigned char DBLSIN_CHARS[] = {213,212,184,190, // Corners
                                205,179, // Horiz & Vert
                                198,181,209,207,216}; // Connectors & Middle

unsigned char * getboxchars(boxtype bt)
{
   switch (bt)
   {
     case BOX_SINSIN: 
          return SINSIN_CHARS;
          break;
     case BOX_DBLDBL:
          return DBLDBL_CHARS;
          break;
     case BOX_SINDBL:
          return SINDBL_CHARS;
          break;
     case BOX_DBLSIN:
          return DBLSIN_CHARS;
          break;
     default:
          return SINSIN_CHARS;
          break;
   }
   return SINSIN_CHARS;
}

// Draw box and lines
void drawbox(char top,char left,char bot, char right, 
             char page, char attr,boxtype bt)
{
   unsigned char *box_chars; // pointer to array of box chars
   unsigned char x;
    
  box_chars = getboxchars(bt);
  // Top border
  gotoxy(top,left,page);
  cprint(box_chars[BOX_TOPLEFT],attr,1,page);
  gotoxy(top,left+1,page);
  cprint(box_chars[BOX_TOP],attr,right-left,page);
  gotoxy(top,right,page);
  cprint(box_chars[BOX_TOPRIGHT],attr,1,page);
  // Bottom border
  gotoxy(bot,left,page);
  cprint(box_chars[BOX_BOTLEFT],attr,1,page);
  gotoxy(bot,left+1,page);
  cprint(box_chars[BOX_BOT],attr,right-left,page);
  gotoxy(bot,right,page);
  cprint(box_chars[BOX_BOTRIGHT],attr,1,page);
  // Left & right borders
  for (x=top+1; x < bot; x++)
    {
      gotoxy(x,left,page);
      cprint(box_chars[BOX_LEFT],attr,1,page);
      gotoxy(x,right,page);
      cprint(box_chars[BOX_RIGHT],attr,1,page);
    }
}

void drawhorizline(char top, char left, char right, char page, char attr, 
                   boxtype bt, char dumb)
{
  unsigned char start,end;
  unsigned char *box_chars = getboxchars(bt);
  if (dumb==0) {
    start = left+1;
    end = right-1;
  } else {
    start = left;
    end = right;
  }
  gotoxy(top,start,page);
  cprint(box_chars[BOX_HORIZ],attr,end-start+1,page);
  if (dumb == 0)
  {
    gotoxy(top,left,page);
    cprint(box_chars[BOX_LTRT],attr,1,page);
    gotoxy(top,right,page); 
    cprint(box_chars[BOX_RTLT],attr,1,page);
  }
}
