/* 
 *    Programmed By: Mohammed Isam Mohammed [mohammed_isam1984@yahoo.com]
 *    Copyright 2014 (c)
 * 
 *    file: kbd.c
 *    This file is part of the GnuDOS project.
 *
 *    GnuDOS 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 3 of the License, or
 *    (at your option) any later version.
 *
 *    GnuDOS 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 GnuDOS.  If not, see <http://www.gnu.org/licenses/>.
 */    
#include "kbd.h"
#include <pthread.h>

/*int button;

typedef struct {
  int x;
  int y;
} mouse;
mouse pos;

pthread_t tid;
int kbd_buf[1024];
int MAX_KBD_BUF = 1024;
int kbd_buf_len = 0;
int end;
*/

static struct termios tty_attr_old;
static int old_keyboard_mode;

int getKeyUnderConsole();
int getKeyUnderX();
//void* _getKey(void *arg);
//void add_mouse_action();

/*void add_mouse_action() {
  if(X_IS_RUNNING) {
	  char b = getchar();
	  char b1 = (b & 3);	//mask the lower 2 bits ==> 00000011B
	  if(b1 == 0) button = 1;
	  if(b1 == 1) button = 2;
	  if(b1 == 2) button = 3;
	  if(b1 == 3) button = 0;
	  if(b & 4) SHIFT = 1;
	  if(b & 8) ALT = 1;
	  if(b & 16) CTRL = 1;
	  char c = getchar();
	  pos.x = c-32;
	  c = getchar();
	  pos.y = c-32;
	  //printf("%d, %d:%d\n", button, pos.x, pos.y);
	  mouse_handler(button, pos);
  }
}
*/

/******************************************
 * This function initiates a new thread
 * to watch for keyboard input and put it
 * into kbd_buf[] for the getKey() function
 * to retrieve it later.
 * ****************************************/
/*int init_kbd() {
  kbd_buf_len = 0;
  end = 0;
  if(tid) return 0;
  if(!initTerminal()) return 0;
  if(pthread_create(&tid, NULL, &_getKey, NULL) != 0)
    return 0;
  return 1;
}

void restore_kbd() {
  end = 1;
  pthread_join(tid, NULL);
  restore_mouse();
  restoreTerminal();
}
*/

/*******************************************
 * Shadow function that fills the keyboard
 * buffer with characters, to be retrieved
 * by proper function getKey().
 * *****************************************/
/*void* _getKey(void *arg) {
  while(!end) {
    if(kbd_buf_len < 0) kbd_buf_len = 0;
    if(X_IS_RUNNING) kbd_buf[kbd_buf_len] = getKeyUnderX();
    else { kbd_buf[kbd_buf_len] = getKeyUnderConsole();
      //int c = Gpm_Getc(stdin);
    }
    if(kbd_buf[kbd_buf_len] != 0) kbd_buf_len++;
    else continue;
    //check buffer length and shift if necessary
    if(kbd_buf_len >= MAX_KBD_BUF) {
      int i;
      for(i = 0; i < MAX_KBD_BUF-1; i++)
	kbd_buf[i] = kbd_buf[i+1];
      kbd_buf_len--;
    }
  }//end while
  int ret = 1;
  pthread_exit(&ret);
}
*/


int initTerminal()
{
    struct termios tty_attr;
    int flags;

    /* make stdin non-blocking */
    flags = fcntl(0, F_GETFL);
    //flags |= O_NONBLOCK;
    fcntl(0, F_SETFL, flags);

    /* save old keyboard mode */
    if (ioctl(0, KDGKBMODE, &old_keyboard_mode) < 0) 
    {
//	return 0;
	X_IS_RUNNING = 1;
    } 
    else X_IS_RUNNING = 0;

    tcgetattr(0, &tty_attr_old);

    /* turn off buffering, echo and key processing */
    tty_attr = tty_attr_old;
    tty_attr.c_lflag &= ~(ICANON | ECHO | ISIG);
    tty_attr.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
    tcsetattr(0, TCSANOW, &tty_attr);

    if(!X_IS_RUNNING) ioctl(0, KDSKBMODE, K_RAW);
    //ioctl(0, KDSKBMODE, K_UNICODE);
    ALT = 0; 
    CTRL = 0; 
    SHIFT = 0;
    return 1;
}

void restoreTerminal()
{
    if(X_IS_RUNNING) 
    {
	    tcsetattr(0, TCSANOW, &tty_attr_old);
    } 
    else 
    {
	    tcsetattr(0, TCSAFLUSH, &tty_attr_old);
	    ioctl(0, KDSKBMODE, old_keyboard_mode);
    }
}//end restoreTerminal()

int getKeyUnderConsole()
{
    char buf[1];
    int res, i = 0;
    res = read(0, &buf[0], 1);
//    while (1) {
	switch (buf[0]) 
	{
	//scancodes for keypresses
	case 0x01: return ESC_KEY; //printf("ESC was pressed"); break;
	case 0x29 : return SHIFT?'~':'`'; //printf("` was pressed"); break;
	case 0x02 : return SHIFT?'!':'1'; //printf("1 was pressed"); break;
	case 0x03 : return SHIFT?'@':'2'; //printf("2 was pressed"); break;
	case 0x04 : return SHIFT?'#':'3'; //printf("3 was pressed"); break;
	case 0x05 : return SHIFT?'$':'4'; //printf("4 was pressed"); break;
	case 0x06 : return SHIFT?'%':'5'; //printf("5 was pressed"); break;
	case 0x07 : return SHIFT?'^':'6'; //printf("6 was pressed"); break;
	case 0x08 : return SHIFT?'&':'7'; //printf("7 was pressed"); break;
	case 0x09 : return SHIFT?'*':'8'; //printf("8 was pressed"); break;
	case 0x0a : return SHIFT?'(':'9'; //printf("9 was pressed"); break;
	case 0x0b : return SHIFT?')':'0'; //printf("0 was pressed"); break;
	case 0x0c : return SHIFT?'_':'-'; //printf("- was pressed"); break;
	case 0x0d : return SHIFT?'+':'='; //printf("= was pressed"); break;
	case 0x0e : return BACKSPACE_KEY; //printf("BKSP was pressed"); break;
	case 0x0f : return TAB_KEY; //printf("TAB was pressed"); break;
	case 0x10 : return SHIFT?'Q':'q'; //printf("q was pressed"); break;
	case 0x11 : return SHIFT?'W':'w'; //printf("w was pressed"); break;
	case 0x12 : return SHIFT?'E':'e'; //printf("e was pressed"); break;
	case 0x13 : return SHIFT?'R':'r'; //printf("r was pressed"); break;
	case 0x14 : return SHIFT?'T':'t'; //printf("t was pressed"); break;
	case 0x15 : return SHIFT?'Y':'y'; //printf("y was pressed"); break;
	case 0x16 : return SHIFT?'U':'u'; //printf("u was pressed"); break;
	case 0x17 : return SHIFT?'I':'i'; //printf("i was pressed"); break;
	case 0x18 : return SHIFT?'O':'o'; //printf("o was pressed"); break;
	case 0x19 : return SHIFT?'P':'p'; //printf("p was pressed"); break;
	case 0x1a : return SHIFT?'{':'['; //printf("[ was pressed"); break;
	case 0x1b : return SHIFT?'}':']'; //printf("] was pressed"); break;
	case 0x1c : return ENTER_KEY; //printf("ENTER was pressed"); break;
	case 0x3a : return CAPS_KEY; //printf("CAPS was pressed"); break;
	case 0x1e : return SHIFT?'A':'a'; //printf("a was pressed"); break;
	case 0x1f : return SHIFT?'S':'s'; //printf("s was pressed"); break;
	case 0x20 : return SHIFT?'D':'d'; //printf("d was pressed"); break;
	case 0x21 : return SHIFT?'F':'f'; //printf("f was pressed"); break;
	case 0x22 : return SHIFT?'G':'g'; //printf("g was pressed"); break;
	case 0x23 : return SHIFT?'H':'h'; //printf("h was pressed"); break;
	case 0x24 : return SHIFT?'J':'j'; //printf("j was pressed"); break;
	case 0x25 : return SHIFT?'K':'k'; //printf("k was pressed"); break;
	case 0x26 : return SHIFT?'L':'l'; //printf("l was pressed"); break;
	case 0x27 : return SHIFT?':':';'; //printf("; was pressed"); break;
	case 0x28 : return SHIFT?'"':'\''; //printf("' was pressed"); break;
	case 0x2b : return SHIFT?'|':'\\'; //printf("\\ was pressed"); break;
	case 0x2a : SHIFT = 1; return SHIFT_DOWN; //printf("LSH was pressed"); break;
	case 0x56 : return SHIFT?'>':'<'; //printf("< was pressed"); break;
	case 0x2c : return SHIFT?'Z':'z'; //printf("z was pressed"); break;
	case 0x2d : return SHIFT?'X':'x'; //printf("x was pressed"); break;
	case 0x2e : return SHIFT?'C':'c'; //printf("c was pressed"); break;
	case 0x2f : return SHIFT?'V':'v'; //printf("v was pressed"); break;
	case 0x30 : return SHIFT?'B':'b'; //printf("b was pressed"); break;
	case 0x31 : return SHIFT?'N':'n'; //printf("n was pressed"); break;
	case 0x32 : return SHIFT?'M':'m'; //printf("m was pressed"); break;
	case 0x33 : return SHIFT?'<':','; //printf(", was pressed"); break;
	case 0x34 : return SHIFT?'>':'.'; //printf(". was pressed"); break;
	case 0x35 : return SHIFT?'?':'/'; //printf("/ was pressed"); break;
	case 0x36 : SHIFT = 1; return SHIFT_DOWN; //printf("RSH was pressed"); break;
	case 0x1d : CTRL = 1; return 0; //printf("LCT was pressed"); break;
	case 0x38 : ALT = 1; return 0; //printf("LAL was pressed"); break;
	case 0x39 : return SPACE_KEY; //printf("SPACE was pressed"); break;
	//scancodes for keyreleases
/*	case -126: printf("1 was released"); break;
	case -125: printf("2 was released"); break;
	case -124: printf("3 was released"); break;
	case -123: printf("4 was released"); break;
	case -122: printf("5 was released"); break;
	case -121: printf("6 was released"); break;
	case -120: printf("7 was released"); break;
	case -119: printf("8 was released"); break;
	case -118: printf("9 was released"); break;
	case -117: printf("0 was released"); break;
	case -116: printf("- was released"); break;
	case -115: printf("= was released"); break;
	case -114: printf("BKSP was released"); break;
	case -113: printf("TAB was released"); break;
	case -112: printf("q was released"); break;
	case -111: printf("w was released"); break;
	case -110: printf("e was released"); break;
	case -109: printf("r was released"); break;
	case -108: printf("t was released"); break;
	case -107: printf("y was released"); break;
	case -106: printf("u was released"); break;
	case -105: printf("i was released"); break;
	case -104: printf("o was released"); break;
	case -103: printf("p was released"); break;
	case -102: printf("[ was released"); break;
	case -101: printf("] was released"); break;
	case -100: printf("ENTER was released"); break;
	case -98: printf("a was released"); break;
	case -97: printf("s was released"); break;
	case -96: printf("d was released"); break;
	case -95: printf("f was released"); break;
	case -94: printf("g was released"); break;
	case -93: printf("h was released"); break;
	case -92: printf("j was released"); break;
	case -91: printf("k was released"); break;
	case -90: printf("l was released"); break;
	case -89: printf("; was released"); break;
	case -88: printf("' was released"); break;
	case -87: printf("` was released"); break;
	case -85: printf("\\ was released"); break;
	case -70: printf("CAPS was released"); break;
	case -84: printf("z was released"); break;
	case -83: printf("x was released"); break;
	case -82: printf("c was released"); break;
	case -81: printf("v was released"); break;
	case -80: printf("b was released"); break;
	case -79: printf("n was released"); break;
	case -78: printf("m was released"); break;
	case -77: printf(", was released"); break;
	case -76: printf(". was released"); break;
	case -75: printf("/ was released"); break;
	case -42: printf("< was released"); break;*/
	case -86: SHIFT = 0; return SHIFT_UP; //printf("LSH was released"); break;
	case -99: CTRL = 0; return 0; //printf("LCT was released"); break;
	case -72: ALT = 0; return 0; //printf("LAL was released"); break;
//	case -71: printf("SPACE was released"); break;
	case -74: SHIFT = 0; return SHIFT_UP; //printf("RSH was released"); break;
	case -32: res = read(0, &buf[0], 1);
//		printf(" %d", buf[0]);
		if(buf[0] == 73) return PGUP_KEY; //printf("UP was pressed");
		if(buf[0] == 81) return PGDOWN_KEY; //printf("UP was pressed");
		if(buf[0] == 72) return UP_KEY; //printf("UP was pressed");
		if(buf[0] == 71) return HOME_KEY; //printf("UP was pressed");
		if(buf[0] == 79) return END_KEY; //printf("UP was pressed");
		if(buf[0] == 82) return INS_KEY; //printf("UP was pressed");
		if(buf[0] == 83) return DEL_KEY; //printf("UP was pressed");
//		if(buf[0] == -56) printf("UP was released");
		if(buf[0] == 75) return LEFT_KEY; //printf("LF was pressed");
//		if(buf[0] == -53) printf("LF was released");
		if(buf[0] == 80) return DOWN_KEY; //printf("DN was pressed");
//		if(buf[0] == -48) printf("DN was released");
		if(buf[0] == 77) return RIGHT_KEY; //printf("RT was pressed");
//		if(buf[0] == -51) printf("RT was released");
		if(buf[0] == 29) CTRL = 1; return 0; //printf("RCT was pressed");
		if(buf[0] == -99) CTRL = 0; return 0; //printf("RCT was released");
		if(buf[0] == 56) ALT = 1; return 0; //printf("RAL was pressed");
		if(buf[0] == -72) ALT = 0; return 0; //printf("RAL was released");
	//default: locate(2,2); printf(" %d", buf[0]); break;
	}
	//res = read(0, &buf[0], 1);
    //}
 return 0;
}

int getKeyUnderX() 
{
  int c;
  ALT = 0; CTRL = 0; SHIFT = 0;
    //while(1) {//!end){
      c = getchar();
      if(c < 0) return 0; //continue;
      //fprintf(stdout, "..%c, %d..", c, c);
 	      if(c == 127) { return BACKSPACE_KEY; }
 	      if(c == 26) { CTRL=1; return 'z'; }//{ return CTRL_S_KEY; }
 	      if(c == 25) { CTRL=1; return 'y'; }//{ return CTRL_S_KEY; }
 	      if(c == 04) { CTRL=1; return 'd'; }//{ return CTRL_S_KEY; }
 	      if(c == 03) { CTRL=1; return 'c'; }//{ return CTRL_S_KEY; }
 	      if(c == 24) { CTRL=1; return 'x'; }//{ return CTRL_X_KEY; }
 	      if(c == 23) { CTRL=1; return 'w'; }//{ return CTRL_V_KEY; }
 	      if(c == 22) { CTRL=1; return 'v'; }//{ return CTRL_V_KEY; }
 	      if(c == 15) { CTRL=1; return 'o'; }//{ return CTRL_O_KEY; }
 	      if(c == 16) { CTRL=1; return 'p'; }//{ return CTRL_P_KEY; }
 	      if(c == 06) { CTRL=1; return 'f'; }//{ return CTRL_F_KEY; }
 	      if(c == 05) { CTRL=1; return 'e'; }//{ return CTRL_F_KEY; }
 	      if(c == 01) { CTRL=1; return 'a'; }//{ return CTRL_A_KEY; }
 	      if(c == 19) { CTRL=1; return 's'; }//{ return CTRL_S_KEY; }
 	      if(c == 18) { CTRL=1; return 'r'; }//{ return CTRL_S_KEY; }
 	      if(c == 14) { CTRL=1; return 'n'; }//{ return CTRL_S_KEY; }
 	      if(c == 17) { CTRL=1; return 'q'; }//{ return CTRL_S_KEY; }
 	      if(c == 32) 
	      {	//the SPACEBAR is pressed
		return SPACE_KEY;
	      }
	      if(c == 10 || c == 13) 
	      {	//the ENTER is pressed
		return ENTER_KEY;
	      }
	      if(c == 9) 
	      {	//the TAB is pressed
		return TAB_KEY;
	      }
	  if(c == 27) 
	  {	//ESC key pressed -- maybe starting an escape sequence??
	  c = getchar();
	  //fprintf(stdout, "..%c, %d..", c, c);
	  if((c == 'f') || (c == 'F')) { ALT=1; return 'f'; }//{ return ALT_F_KEY; }
	  if((c == 'e') || (c == 'E')) { ALT=1; return 'e'; }//{ return ALT_E_KEY; }
	  if((c == 'h') || (c == 'H')) { ALT=1; return 'h'; }//{ return ALT_H_KEY; }
	  if((c == 'o') || (c == 'O')) { ALT=1; return 'o'; }//{ return ALT_O_KEY; }
	  if(c == 91) 
	  {	//yep -- this is the left bracket '[' -- so there is something coming
	    c = getchar();
	    //fprintf(stdout, "..%c, %d..", c, c);
//	      if(c == 'M') { add_mouse_action(); return 0; }
	      if(c == 65) { return UP_KEY; }
	      if(c == 66) { return DOWN_KEY; }
	      if(c == 67) { return RIGHT_KEY; }
	      if(c == 68) { return LEFT_KEY; }
	      if(c == 72) { return HOME_KEY; }
	      if(c == 70) { return END_KEY; }
	      if(c == 54) { c = getchar(); return PGDOWN_KEY; }
	      if(c == 53) { c = getchar(); return PGUP_KEY; }
	      if(c == 50) { c = getchar(); return INS_KEY; }
	      if(c == 51) { c = getchar(); return DEL_KEY; }
	      if(c == 49) 
	      { //this is CTRL-something
		CTRL = 1; 
		c = getchar();
		//fprintf(stdout, "..%c, %d..", c, c);
		if(c == 59) 
		{
		  c = getchar();
		  if(c == 53) 
		  {
		    c = getchar();
		    if(c == 67) return RIGHT_KEY; 
		    if(c == 68) return LEFT_KEY; 
		    if(c == 65) return UP_KEY; 
		    if(c == 66) return DOWN_KEY; 
		    if(c == 72) return HOME_KEY; 
		    if(c == 70) return END_KEY; 
		  }
		}//end if
	  }//end if(c == 49)
	}//end if(c == 91)
	return ESC_KEY;
      }//end if(c == 27)
      if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')  ||
	(c >= 32 && c<= 64) || (c >=123 && c <= 126))	//it is alphanumeric
	return c;
    //}
    return 0;
}


/*int getKey() {
  if(kbd_buf_len <= 0) return 0;
  int c, i;
  c = kbd_buf[0];
  for(i = 0; i < kbd_buf_len-2; i++)
    kbd_buf[i] = kbd_buf[i+1];
  kbd_buf_len--;
  return c;
}*/

int getKey() 
{
  if(X_IS_RUNNING) return getKeyUnderX();
  else return getKeyUnderConsole();
}