#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#if 0
typedef struct _INPUT_RECORD { // ir
    WORD EventType;
    union {
        KEY_EVENT_RECORD KeyEvent;
        MOUSE_EVENT_RECORD MouseEvent;
        WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
        MENU_EVENT_RECORD MenuEvent;
        FOCUS_EVENT_RECORD FocusEvent;
    } Event;
} INPUT_RECORD;
typedef struct _KEY_EVENT_RECORD {
    BOOL bKeyDown;
    WORD wRepeatCount;
    WORD wVirtualKeyCode;
    WORD wVirtualScanCode;
    union {
        WCHAR UnicodeChar;
        CHAR  AsciiChar;
    } uChar;
    DWORD dwControlKeyState;
} KEY_EVENT_RECORD;

typedef struct _MOUSE_EVENT_RECORD {
    COORD dwMousePosition;
    DWORD dwButtonState;
    DWORD dwControlKeyState;
    DWORD dwEventFlags;
} MOUSE_EVENT_RECORD;


#endif

/*
 * dwControlKeyState
 * 
 * Indicates the state of the control keys. This member can be a
 * combination of the following values:
 * 
 * CAPSLOCK_ON        The CAPS LOCK light is on.
 * ENHANCED_KEY       The key is enhanced.
 * LEFT_ALT_PRESSED   The left ALT key is pressed.
 * LEFT_CTRL_PRESSED  The left CTRL key is pressed.
 * NUMLOCK_ON         The NUM LOCK light is on.
 * RIGHT_ALT_PRESSED  The right ALT key is pressed.
 * RIGHT_CTRL_PRESSED The right CTRL key is pressed.
 * SCROLLLOCK_ON      The SCROLL LOCK light is on.
 * SHIFT_PRESSED      The SHIFT key is pressed.
 * 
 * Enhanced keys for the IBM 101/102 keyboards are the INS, DEL, HOME,
 * END, PAGE UP, PAGE DOWN, and direction keys in the clusters to the
 * left of the keypad; and the DIVIDE and ENTER keys in the keypad.
 */

#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
#define IsKey(x) ((keystate & x) != 0)

char keyDescription[256];
char *translate(c,keycode,keystate) int c,keycode; long keystate; {
char buf[256]; char *p;

p=(char *)&keyDescription[0]; p[0]=0;
if(IsKey(ENHANCED_KEY)) strcat(p,"GREY ");
if(IsKey(ALT_PRESSED)) strcat(p,"ALT ");
if(IsKey(CTRL_PRESSED)) strcat(p,"CTRL ");
if(IsKey(CAPSLOCK_ON)){ 
  strcat(p,"CapsLOCK ");
  if(IsKey(SHIFT_PRESSED)) c=tolower(c); else c=toupper(c);
}
if(IsKey(SHIFT_PRESSED)) {
  strcat(p,"SHIFT ");
  if(IsKey(CAPSLOCK_ON)) c=tolower(c); else c=toupper(c);
}
switch(c){
case '+': if(keycode==78) strcat(p,"GREY "); break;
case '-': if(keycode==74) strcat(p,"GREY "); break;
case '*': if(keycode==55) strcat(p,"GREY "); break;
};
if(c != 0) {
  if(isprint(c)) sprintf(buf,"%c",c);
  else sprintf(buf,"0x%04x ",c);
  strcat(p,buf);
} else {
  if(IsKey(CTRL_PRESSED)){
    switch(keycode){
    case 53: c='/'; break;
    case 7:  c='^'; break;
    case 12: c='_'; break;
    };
  }
  if(c != 0){ sprintf(buf,"%c",c); strcat(p,buf);}
}
return p;
};

char keySeq[256];
char *EscSequence(c,keycode,keystate) int c,keycode; long keystate; {
char buf[256]; char *p;

p=(char *)&keySeq[0]; p[0]=0;
if(IsKey(ALT_PRESSED)) strcat(p,"\\E");
if(IsKey(ENHANCED_KEY)){};
if(IsKey(CTRL_PRESSED)) {};
if(IsKey(CAPSLOCK_ON)){ 
  if(IsKey(SHIFT_PRESSED)) c=tolower(c); else c=toupper(c);
}
if(IsKey(SHIFT_PRESSED)) {
  if(IsKey(CAPSLOCK_ON)) c=tolower(c); else c=toupper(c);
}
switch(c){
case '+': if(keycode==78) ;  break;
case '-': if(keycode==74) ; break;
case '*': if(keycode==55) ; break;
};
if(c == 0) {
  if(IsKey(CTRL_PRESSED)){
    switch(keycode){
    case 53: c='/'; break;
    case 7:  c='^'; break;
    case 12: c='_'; break;
    };
  }
}
return p;
};


char bits[64];
char * binary(keystate) long keystate; {
char *p = (char *)&bits[0];
int i;
  p[32]=0;
  for(i=0;i<32;++i){
    p[31-i]= ((1 & keystate) != 0) ? '1' : '0';
    keystate = (keystate >> 1);
  }
  return p;
}

void keybanger(){
int c;
HANDLE hStdin,hStdout;
INPUT_RECORD Buffer;
PINPUT_RECORD Buf= &Buffer;
long NumRead;
COORD mp;

    hStdout=GetStdHandle(STD_OUTPUT_HANDLE);
    hStdin=GetStdHandle(STD_INPUT_HANDLE);
    SetConsoleMode(hStdin,0 /*ENABLE_MOUSE_INPUT*/); /* noecho, no ctrl-c */
    /* ReadConsoleInput waits for an event. Ctrl-C is not passed through
       unless ENABLE_PROCESSED_INPUT has been turned off */
    while(1){
    int result,scancode; long keystate; char *p;
      result=ReadConsoleInput(hStdin,Buf,(DWORD)1,(LPDWORD)&NumRead);
      if(result==0) {printf("Got zero\n");continue;}
        if(Buffer.EventType==KEY_EVENT){
          c=Buffer.Event.KeyEvent.uChar.AsciiChar;
          scancode=Buffer.Event.KeyEvent.wVirtualScanCode;
          keystate=Buffer.Event.KeyEvent.dwControlKeyState;
          if(Buffer.Event.KeyEvent.bKeyDown==0){
            printf("Key = %04x, scan=%d, ",c,scancode);
            p=translate(c,scancode,keystate);
            printf("keystate=%s, Trans='%s'\n",binary(keystate),p);
            if(c == 'Q' || c == 'q') break;
            if(c == 3) printf("Use Q or q to quit\n");
          }
      }
      if(Buffer.EventType==MOUSE_EVENT){
        mp=Buffer.Event.MouseEvent.dwMousePosition;
        printf("Mouse event, x=%d, y=%d\n",mp.X,mp.Y);
          }
    }

    printf("All done with keybanger\n");
}

/* Once stdout has been redefined to point to the current console, */
/* then all printf output goes to that console.                    */

void launchconsole(){
int hCrt; FILE *hf; long hStdout;
  FreeConsole();
  AllocConsole();            /* Open a console window */
  hStdout=(long)GetStdHandle(STD_OUTPUT_HANDLE);
  hCrt= _open_osfhandle(hStdout,_O_TEXT); /* Get file descriptor */
  hf= fdopen(hCrt,"w" ); /* Get Ansi-C handle for file descriptor*/
  *stdout = *hf;            /* Change stdout to this one */
  setvbuf( stdout, NULL, _IONBF, 0 ); /* no buffering */
}

int main(void){
#if 1
  (void)launchconsole();
#endif
  (void)keybanger();
  return 0; // C runtime cleans up the file opens
}
