/* Keyboard support routines.
	for Windows NT system.

   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.  

   */
#ifndef _OS_NT
#error This file is for the NT operating system.
#else

#include <windows.h>
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include "tty.h"
#include <ctype.h>
#include <errno.h>
#include <malloc.h> 
#include "util.h"		/* For xmalloc prototype */
#include "mad.h"		/* The memory debugger */
#include "global.h"
#include "mouse.h"
#include "key.h"
#include "main.h"
#include "file.h"
#include "../vfs/vfs.h"

/* "$Id: key.c,v 1.18 1995/02/21 19:06:13 miguel Exp $" */
int old_esc_mode = 0;
int double_click_speed;		/* this two are not used... they are here to keep linker happy */
int mou_auto_repeat;
int use_8th_bit_as_meta = 0;

/*  Prototypes */
static int EscapeKey (char* seq);
static int ControlKey (char* seq);
static int AltKey (char *seq);

/* Static Tables */
struct {
    int key_code;
    int vkcode;
} key_table [] = {
    { KEY_F(1),  VK_F1 },
    { KEY_F(2),  VK_F2 },
    { KEY_F(3),  VK_F3 },
    { KEY_F(4),  VK_F4 },
    { KEY_F(5),  VK_F5 },
    { KEY_F(6),  VK_F6 },
    { KEY_F(7),  VK_F7 },
    { KEY_F(8),  VK_F8 },
    { KEY_F(9),  VK_F9 },
    { KEY_F(10), VK_F10 },
    { KEY_F(11), VK_F11 },
    { KEY_F(12), VK_F12 },
    { KEY_F(13), VK_F13 }, 
    { KEY_F(14), VK_F14 },
    { KEY_F(15), VK_F15 },
    { KEY_F(16), VK_F16 },
    { KEY_F(17), VK_F17 },
    { KEY_F(18), VK_F18 },
    { KEY_F(19), VK_F19 },
    { KEY_F(20), VK_F20 },	
    { KEY_IC,    VK_INSERT },		
    { KEY_DC,    VK_DELETE },
    { KEY_BACKSPACE, VK_BACK },
    { '\t',   VK_TAB },
    { KEY_ENTER, VK_RETURN },
    { KEY_ENTER, VK_EXECUTE },
//    { KEY_PRINT, VK_SNAPSHOT },

    { KEY_PPAGE, VK_PRIOR },		// Movement keys
    { KEY_NPAGE, VK_NEXT },
    { KEY_LEFT,  VK_LEFT },
    { KEY_RIGHT, VK_RIGHT },
    { KEY_UP,    VK_UP },
    { KEY_DOWN,  VK_DOWN },
    { KEY_HOME,  VK_HOME },
    { KEY_END,	 VK_END },

    { KEY_KP_MULTIPLY,  VK_MULTIPLY },		// Numeric pad
    { KEY_KP_ADD,  VK_ADD },
    { KEY_KP_SUBTRACT,  VK_SUBTRACT },
//    { ,  VK_DIVIDE },

/* Control key codes */
    { 0, VK_CONTROL },			/* Control */
    { 0, VK_MENU },			/* Alt     */
    { 0, VK_ESCAPE },			/* ESC	   */

/* Key codes to ignore */
    { -1, VK_SHIFT },			/* Shift when released generates a key event */
    { -1, VK_CAPITAL },			/* Caps-lock */

#if WINVER >= 0x400			/* new Chicago key codes (not in 3.x headers) */
    { -1, VK_APPS },			/* "Application key" */
    { -1, VK_LWIN },			/* Left "Windows" key */
    { -1, VK_RWIN },			/* Right "Windows" key */
#endif

    { 0, 0}
};		

/* Special handlers for control key codes 
	Note that howmany must be less than seq_buffer len
	ESC is not being handled right now. (S-lang returns ascii \e)
*/
struct {
    int vkcode;
    int (*func_hdlr)(char *);	
    int howmany;
} key_control_table[] = {
    { VK_ESCAPE, EscapeKey, 1 },
    { VK_CONTROL, ControlKey, 1 },
    { VK_MENU, AltKey, 1 },
    { 0, NULL, 0},
};

void try_channels (int set_timeout)
{
}
void channels_up()
{
}
void channels_down()
{
}

void init_key (void)
{
}

/* The maximum sequence length (32 + null terminator) */
static int seq_buffer[33];
static int *seq_append = 0;

static int push_char (int c)
{
    if (!seq_append)
	seq_append = seq_buffer;
    
    if (seq_append == &(seq_buffer [sizeof (seq_buffer)-2]))
	return 0;
    *(seq_append++) = c;
    *seq_append = 0;
    return 1;
}

void define_sequence (int code, char* vkcode, int action)
{
}

static int *pending_keys;
int AltIsDown = 0;

int correct_key_code (int c)
{
    switch (c) {
        case KEY_KP_ADD: c = alternate_plus_minus ? ALT('+') : '+'; break;
        case KEY_KP_SUBTRACT: c = alternate_plus_minus ? ALT('-') : '-'; break;
        case KEY_KP_MULTIPLY: c = alternate_plus_minus ? ALT('*') : '*'; break;
	case -1:
		c = 0;
		break;
	default:
		break;
    }

    return c;
}

int get_key_code (int no_delay)
{
    int c, i, k, j;
    static int lastnodelay = -1;
    
    if (no_delay != lastnodelay) {
        lastnodelay = no_delay;
    }

 pend_send:
    if (pending_keys) {
	int d = *pending_keys++;
 check_pend:
	if (!*pending_keys){
	    pending_keys = 0;
	    seq_append = 0;
	}
/*	if (d == ESC_CHAR && pending_keys){
	    d = ALT(*pending_keys++);
	    goto check_pend;
	}
	if ((d & 0x80) && use_8th_bit_as_meta)
	    d = ALT(d & 0x7f);
	this = NULL;
*/	return correct_key_code (d);
    }

    if (no_delay) {
        nodelay (stdscr, TRUE);
    }

    c = xgetch ();
    if (no_delay) {
        nodelay (stdscr, FALSE);
        if (c == ERR) 
            return ERR;

    } else if (c == ERR){
	/* Maybe we got an incomplete match.
	   This we do only in delay mode, since otherwise
	   xgetch can return ERR at any time. */
	return ERR;
    }
    /* S-Lang will return 0 and then a VK_CODE if char is not ascii 
       That is because some VK codes are same as lower-case ascii (e.g. VK_F?) */
    if (c)    
    	return c;
    else
	c = xgetch();		   

    // Replace key code with that in table
    for (i=0;  key_table[i].vkcode != 0 || key_table[i].key_code != 0; i++) 
	if (c == key_table[i].vkcode) {
		if (key_table[i].key_code)
			return correct_key_code (key_table[i].key_code);
		else {
		    // We special entries have 0 key_code, search them in control keys table
		    for (j=0; key_control_table[j].vkcode != 0; j++) {
		    	if (key_control_table[j].vkcode == c) {
				/* Save as many keystrokes as asked */
				pending_keys = seq_append = NULL;
				for (k=0; k < key_control_table[j].howmany; k++)
				    push_char(xgetch());
				/* Call special handler */
				c = key_control_table[j].func_hdlr (seq_buffer);
				if (!c)	{	/* handler asks us to return all the saved sequence */
				    pending_keys = seq_buffer;
				    goto pend_send;
				}
			}
		    }
		}
	}

    return correct_key_code (c);
}

static int getch_with_delay (void)
{
    int c;

    while (1) {
	/* Try to get a character */
	c = get_key_code (0);
	if (c != ERR)
	    break;
    }
    /* Success -> return the character */
    return c;
}


extern int max_dirt_limit;

/* Returns a character read from stdin with appropriate interpretation */
/* Also takes care of generated mouse events */
/* Returns 0 if it is a mouse event */
/* The current behavior is to block allways */
int get_event (Gpm_Event *event, int redo_event)
{
#define block 1
    int c;
    static int flag;			/* Return value from select */
    static int dirty = 3;

    if ((dirty == 3) || is_idle ()){
	refresh ();
	doupdate ();
	dirty = 1;
    } else
	dirty++;

    vfs_timeout_handler ();
    
    /* Repeat if using mouse */
#ifdef HAVE_SLANG
    while ((xmouse_flag) && !pending_keys)
    {
	SLms_GetEvent (event);
    }
#endif

#ifndef HAVE_SLANG
#ifdef BUGGY_CURSES
    untouchwin (stdscr);
#endif
#endif

    c = getch_with_delay ();
    if (!c) { 				/* Code is 0, so this is a Control key or mouse event */
#ifdef HAVE_SLANG
	SLms_GetEvent (event);
#else
	ms_GetEvent (event);	/* my curses */
#endif
        return 0;
    }

    return c;
}

/* Returns a key press, mouse events are discarded */
int mi_getch ()
{
    Gpm_Event ev;
    int       key;
    
    while ((key = get_event (&ev, 0)) == 0)
	;
    return key;
}

/* 
  Special handling of ESC key when old_esc_mode:
	ESC+ a-z,\n\t\v\r! = ALT(c)
	ESC + ' '|ESC = ESC
	ESC+0-9 = F(c-'0')
*/
static int EscapeKey (char* seq)
{
    int c = *seq;

    if (old_esc_mode) {
	if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
	  || (c == '\n') || (c == '\t') || (c == XCTRL('h'))
	  || (c == KEY_BACKSPACE) || (c == '!') || (c == '\r')
	  || c == 127 || c == '+' || c == '-' || c == '\\' 
	  || c == '?')
	    c = ALT(c);
	else if (isdigit(c))
	    c = KEY_F (c-'0');
	else if (c == ' ')
 	    c = ESC_CHAR;
	return c;
    }
    else
    	return 0;	/* i.e. return esc then c */
}

/* Control and Alt
 */
static int ControlKey (char* seq)
{
    return XCTRL(*seq);
}
static int AltKey (char *seq)
{
    return ALT(*seq);
}

/* A function to check if we're idle.
   It checks for any waiting event  (that can be a Key, Mouse event, 
   and other internal events like focus or menu) */
int is_idle (void)
{
    DWORD dw;

    static HANDLE hConsoleInput;		 	/* This code should change, we need this handle */
    if (!hConsoleInput)					/* as a global variable. Should think smthng. (and it should be the same as curses or s-lang handle) */
	hConsoleInput = GetStdHandle (STD_INPUT_HANDLE);

    if (GetNumberOfConsoleInputEvents (hConsoleInput, &dw))
	if (dw)
 	    return 0;
    return 1;
}

extern long SLsys_GetLastkeyControlState ();

int ctrl_pressed ()
{
//    if (HIWORD(GetKeyState (VK_CONTROL)))
//	return 1;
//   return 0;
    DWORD dwFlags = (DWORD) SLsys_GetLastkeyControlState();
    if (dwFlags & RIGHT_CTRL_PRESSED ||
	dwFlags & LEFT_CTRL_PRESSED)
	return 1;

    return 0;
}

#endif /* _OS_NT */
