
#include "vt.h"
#include "user.h"

#include <stream.h>
#include <ctype.h>


// VT100 emulation
//
// VT100 code representation:
//   Example: the escape code \e[3;14H  (set cursor position)
//     would be represented by
//           code[] = [,_LN,;,_LN,H
//           VTArgn = 2
//           VTArg[]= 3,14
//     _LN is a flag for a numeric argument, which is kept in VTArg.


// Symbol in code[] which signifies a numeric argument.
const int _LN = -1;

int VT100emulator::write (char* buf, int nchars)
{ 
  for (int i = 0; i < nchars; i++) userWindow->addch(buf[i]);
}

void VT100emulator::flush ()
{
  userWindow->refresh();
}

// IncrementalWrite calls write for each character in buf.
int VT100emulator::IncrementalWrite (char* buf, int nchars)
{
  int ccount =0;
  for(int i=0;i<nchars;i++) {
    int res = write(&buf[i],1);
    if(res==-1) return -1;
    if(res!=1)  return -1;
    ccount += res;
  }
  return ccount;
}

// Regenerates and prints the code which was just parsed and recognized.
//   Equivalent expressions may be substituted.
void VT100emulator::send_last_code()
{
  switch(last) {
  case c_UnkOp:
    break;
  case c_NotCode:
    break;
  case c_InCode:
    break;
  case c_CUP:    
    if(VTArgn==2) userWindow->move(VTArg[0] - 1, VTArg[1] - 1);
    else userWindow->move(VTArg[0] - 1, 0);
    break;
  case c_ED:    // Clear to end of screen
    userWindow->clrtobot();
    break;
  case c_EL:    // Clear to end of line
    userWindow->clrtoeol();
    break;
//  case c_SGR:    for(int i=0;i<VTArgn;i++) printf("[%dm",VTArg[i]);
  case c_SGR:   // Boldface on or off
//    if (VTArgn > 0) {
//      if (VTArg[0] < 10) userWindow->standout();  // turn it on
//      else userWindow->standend();                // turn it off
//    }
    break;
  default: break;
  }
  fflush(stdout);
  return;
}

// Give a visual indication when an unknown code is recieved.
void VT100emulator::encountered_unknown_code()
{
#ifdef DEBUG
 int *x = code;
 printf("<");
  for(int i=0,j=0;i<codeidx;i++)
    if(x[i]==_LN) { printf("%d.",VTArg[j]); j++; }
    else { printf("%c.",x[i]); }
  printf(">");
  fflush(stdout);
  sleep(6);
#endif
}


// ----- Helper functions for escape code recognition -----

// ----- Matching
// match("foo") returns true if \efoo has been parsed.
// match(123)   returns true if the code so far parsed is 123 characters long.

bool VT100emulator::matched (int len)
{
  return len==codeidx;
}
bool VT100emulator::matched (char* x)
{
  const int len = strlen(x);
  if(len!=codeidx) return 0;
  if(len > 0 && x[0]!=code[0]) if(x[0]!='0'||code[0]!=_LN) return 0;
  if(len > 1 && x[1]!=code[1]) if(x[1]!='0'||code[1]!=_LN) return 0;
  if(len > 2 && x[2]!=code[2]) if(x[2]!='0'||code[2]!=_LN) return 0;
  if(len > 3 && x[3]!=code[3]) if(x[3]!='0'||code[3]!=_LN) return 0;
  if(len > 4 && x[4]!=code[4]) if(x[4]!='0'||code[4]!=_LN) return 0;
  return 1;
}

// ----- Parse results (of this step)
// Was(CODE)  Escape code recognized as CODE
// WasUnk()   Escape code recognized as being unknown.
// IsMore()   Beginning of uncompleted but valid code seen.

inline VT100_code VT100emulator::Was (VT100_code cod)
{
  in_code =0;
  last    =cod;
  return cod;
}
inline VT100_code VT100emulator::WasUnk ()
{
  encountered_unknown_code();
  return Was(c_UnkOp);
}
inline VT100_code VT100emulator::IsMore ()
{
  last = c_InCode;
  return c_InCode;
}

// ----- Recognize VT100 escape codes

VT100_code VT100emulator::filter_escapeCodes(int c)
{
  int i,j;
  int *x = code;

  // Look for leading escape character.
  if(!in_code) {
      if(c!='') return Was(c_NotCode);
      else {
	  in_code =1;
	  VTArgn  =0;
	  codeidx =0;
	  in_code_num =0;
	  last=c_InCode;
	  return last;
      }
  }

  // Continue and termininate numeric arguments.
  if(in_code_num) {
    if(isdigit(c)) VTArg[VTArgn] = VTArg[VTArgn] *10 + (c-48);
    else {
      in_code_num =0;
      VTArgn ++;
      codeidx++;
      x[codeidx] = c;
      codeidx++;
    }
  }
  else {
    // begin numeric argument
    if(isdigit(c)) {
      in_code_num =1;
      x[codeidx] = _LN;
      VTArg[VTArgn] = (c-48);
    }
    // non-numeric character (which doesnt follow number)
    else {
      x[codeidx] = c;
      codeidx++;
    }
  }

  if( matched("0") ) return WasUnk();
  if( matched("=") ) return Was(c_DECPAM);
  if( matched("#") && in_code_num ) return Was(c_UnkOp);
  if( matched("#") ) return IsMore();
  if( matched("[") ) return IsMore();
  if( matched(1)   ) return WasUnk();
  if( matched("[0") ) return IsMore();
  if( matched("[m") ) { VTArg[0]=0;VTArgn=1;
			 return Was(c_SGR); }
  if( matched("[H") ) { VTArg[0]=1;VTArg[1]=1;VTArgn=2;
			 return Was(c_CUP); }
  if( matched("[J") ) { VTArg[0]=0;VTArgn=1;
			 return Was(c_ED); }
  if( matched("[K") ) { VTArg[0]=0;VTArgn=1;
			 return Was(c_EL); }
  if( matched("[?") ) return IsMore();
  if( matched(2)    ) return WasUnk();
  if( matched("[0m") ) return Was(c_SGR);
  if( matched("[0H") ) { VTArgn=2;VTArg[1]=1; return Was(c_CUP); }
  if( matched("[0J") ) return Was(c_ED);
  if( matched("[0K") ) return Was(c_EL);
  if( matched("[0;") ) return IsMore();
  if( matched("[?0") ) return IsMore();
  if( matched(3)     ) return WasUnk();
  if( matched("[0;0") ) return IsMore();
  if( matched("[?0h") ) return Was(c_DECSET);
  if( matched("[?0l") ) return Was(c_DECRST);
  if( matched(4)      ) return WasUnk();
  if( matched("[0;0H") ) return Was(c_CUP);
  if( matched("[0;0m") ) return Was(c_SGR);
  if( matched(5)       ) return WasUnk();
  return WasUnk();
}
  

//=====================================================



void VT100_filter::set_filter_mode(int m)
{
  switch (m) {
  case 0: mode = echo;          break;
  case 1: mode = parsed_echo;   break;
  case 2: mode = filter;        break;
  }
}

void VT100_filter::set_pass_codes(VT100_code *codelist, int how_many)
{
  passcodepop = (maxpop >= how_many) ? how_many : maxpop;
  for(int i=0;i<maxpop;i++) passcodes[i] = codelist[i];
}

int VT100_filter::write (char* buf, int nchars)
{
  if(nchars>1)  return IncrementalWrite(buf,nchars);
  if(nchars==0) return 0;
  char c = buf[0];

  VT100_code code = filter_escapeCodes(c);

  if(mode==echo) {
    userWindow->addch(c);
    return 1;
  }
//    { printf("%c",(int)c);  fflush(stdout); return 1; }
  if(mode==parsed_echo) {
    if(code==c_NotCode) {
      userWindow->addch(c);
      return 1;
    }
    else {
      send_last_code();
      return 1;
    }
  }
//    if(code==c_NotCode) { printf("%c",(int)c); fflush(stdout); return 1; }
//    else                { send_last_code(); return 1; }
  if(mode==filter) {
    for(int i=0;i<maxpop;i++) 
      if(code==passcodes[i]) { send_last_code(); return 1; }
    return 0;
  }
}

//=====================================================

char sbuf[10];
int VT100_from_VT100barton::write (char* buf, int nchars)
{
  if(nchars>1)  return IncrementalWrite(buf,nchars);
  if(nchars==0) return 0;

  static char send_check_buffer[5] = "";
  static int send_num = 0;

  char c = buf[0];

  VT100_code code = filter_escapeCodes(c);

  if(code==c_NotCode) {
      if(c!='' &&
	 (c!=' ' || !on_line_25)) {

	  int in_send = 0;
	  int x, y;

	  userWindow->getyx(y, x);
	  
	  if (y > 18 && x > 60 &&
	      ((send_num == 0 &&
		c == 'S') ||
	       (send_num == 1 &&
		c == 'E') ||
	       (send_num == 2 &&
		c == 'N') ||
	       (send_num == 3 &&
		c == 'D'))) {
	      send_check_buffer[send_num] = c;
	      send_num += 1;
	      in_send = 1;
	  }

	  if (send_num == 4 && in_send)     // found a 'SEND' token
	    {
		userWindow->addch('E');
		userWindow->addch('N');
		userWindow->addch('T');
		userWindow->addch('E');
		userWindow->addch('R');

		send_num = 0;
	    }
	  else if (send_num > 0 && !in_send)  // aborted partial 'SEND' token
	    {
		int i;

		// output partial 'SEND'
		for (i = 0; i < send_num; i++)
		  userWindow->addch(send_check_buffer[i]);
		send_num = 0;
	    }
	  
	  if (!in_send) userWindow->addch(c);
    }
    return 1;
  }
  if(code==c_CUP) {
    int ln = VTArg[0];
    // 
    on_line_25 = (ln==25);
    // Compress out line 2 by sliding everything up.
    if(ln > 2) ln--;

    if(VTArgn==2) userWindow->move(ln - 1, VTArg[1] - 1);
    else userWindow->move(ln - 1, 0);

    return 1;
  }
  else if (code==c_SGR) {
//    if (VTArgn > 0) {
//      if (VTArg[0] < 10) userWindow->standout();
//      else userWindow->standend();
//    }
    return 1;
  }
//  else if (code==c_SGR) return 1; // dont pass boldfaceing.
  else if (code==c_ED) {
      userWindow->clrtobot();
      return 1;
  }
  else if (code==c_EL) {
      userWindow->clrtoeol();
      return 1;
  }
  else {
//    send_last_code();
    return 1;
  }
}
