/* SOURCE: http://www.netbsd.org/docs/Hardware/Machines/DEC/lk201.html */
/* SOURCE: https://lkml.org/lkml/2004/3/16/160 */
/* https://vt100.net/docs/vt100-ug/chapter3.html */

/**********************************************************************/

/* requires LED number data */ 
#define LK_LED_ENABLE   0x13            /* light LED        */ 
#define LK_LED_DISABLE  0x11            /* turn off LED     */ 

#define LED_WAIT        0x81            /* Wait LED         */ 
#define LED_COMP        0x82            /* Compose LED      */ 
#define LED_LOCK        0x84            /* Lock LED         */ 
#define LED_HOLD        0x88            /* Hold Screen LED  */ 
#define LED_ALL         0x8F            /* All LED's        */

/**********************************************************************/

/* Requires volume data byte */
#define LK_CL_ENABLE    0x1B   /* keyclick enable.  Requires volume   */
                               /* byte.  This does not affect the     */
             /* SHIFT key.  The CTRL key requires   */
                               /* LK_CL_ENABLE and LK_CCL_ENABLE to   */
             /* have been sent before it clicks.    */
             /* All other keys are only controlled  */    
             /* by LK_CL_ENABLE.                    */
     
#define LK_CCL_ENABLE   0xBB   /* Enable keyclicks for the CTRL key.  */
                               /* The CTRL keyclick volume is set to  */
             /* be the same as the rest of the keys */
             /* LK_CCL_ENABLE sets a flag in the    */
             /* keyboard with is logically AND'ed   */
             /* with the LK_CL_ENABLE flag to enable*/
             /* CTRL key keyclicks.                 */

#define LK_CL_DISABLE   0x99   /* keyclick disable                    */
#define LK_CCL_DISABLE  0xB9   /* CTRL key keyclick disable           */

#define LK_SOUND_CLICK  0x9F   /* causes the LK201 to sound a keyclick*/

/* max volume is 0, lowest is 0x7 */
#define LK_PARAM_VOLUME(v)              (0x80|((v)&0x7))

/**********************************************************************/

/* requires bell volume data */ 
#define LK_BELL_ENABLE  0x23   /* enable the keyboard bell.  Requires */
                               /* volume data byte.                   */

#define LK_BELL_DISABLE 0xA1   /* disable the keyboard bell.          */

#define LK_RING_BELL    0xA7   /* ring the keyboard bell              */ 

/* max volume is 0, lowest is 0x7 */ 
#define LK_PARAM_VOLUME(v)              (0x80|((v)&0x7))

/**********************************************************************/

#define LK_UPDOWN           0x86 
#define LK_AUTODOWN         0x82 
#define LK_DOWN             0x80 

#define LK_CMD_MODE(m,div)  ((m)|((div)<<3)) 

#define LK_MODECHG_ACK      0xBA    /* sent by the keyboard to acknowledge a  */
                                    /* successful mode change.                */


#define LK_PFX_KEYDOWN      0xB9    /* indicates that the next byte is a key- */
                                    /* code for a key already down in a       */
                                    /* division that has been changed to      */
                                    /* LK_UPDOWN.  I think this means that if */
                                    /* for example, the 'a' key is in LK_DOWN */
                                    /* mode and the key is being held down and*/
                                    /* division 1 is switched to LK_UPDOWN    */
                                    /* mode, the keyboard will produce the    */
                                    /* byte LK_PFX_KEYDOWN followed by 0xC2   */
                                    /* (KEY_A).                               */

#define LK_CMD_RPT_TO_DOWN  0xD9    /* This command causes all divisions which */
                                    /* are programmed for LK_AUTODOWN mode to  */
                                    /* be switched to LK_DOWN mode.            */


#define LK_CMD_ENB_RPT      0xE3    /* enables auto repeat on the keys         */
                                    /* which are in LK_AUTODOWN mode           */

#define LK_CMD_DIS_RPT      0xE1    /* disables auto repeat on all keys, but   */
                                    /* does not change the mode that the       */
                                    /* divisions are programmed to.            */

#define LK_CMD_TMP_NORPT    0xD1    /* temporary auto repeat disable.  This    */
                                    /* command disables auto repeat for the key*/
                                    /* which is currently pressed down.  Auto  */
                                    /* repeat is re-enabled when another key is*/
                                    /* pressed.                                */


#define LK_OUTPUT_ERROR    0xb5
#define LK_INPUT_ERROR      0xB6    /* sent by the keyboard if it receives an  */
                                    /* invalid command.                        */

#define    LK_NO_ERROR         0x00    /* No Error */
#define    LK_KDOWN_ERROR      0x3D    /* Key down on powerup error     */
#define    LK_POWER_ERROR      0x3E    /* Keyboard failure on pwrup tst */

#define LK_ALLUP 0xB3

#define LK_ALL_KEYS_UP    0xb3
#define LK_METRONOME   0xb4

/**********************************************************************/

bool mod_shift = false;
bool mod_ctrl = false;
#define KB_CHAR_SHIFT(key_base, key_shift) ((mod_shift) ? (key_shift) : (key_base))
#define KB_CHAR_CTRL(key_base, key_ctrl) ((mod_ctrl) ? (key_ctrl) : (key_base))
#define KB_CHAR(key_base, key_shift) c = KB_CHAR_SHIFT(key_base, key_shift);
#define KB_CHARC(key_base, key_shift, key_ctrl) c = KB_CHAR_CTRL(KB_CHAR_SHIFT(key_base, key_shift), key_ctrl);

void keyboard_handle_key(int key)
{
  int c = -1; 
  switch (key) {
    /* Key Division 191-255 */
    case 191: KB_CHAR('`', '~'); break; // (xbf): KEY_TILDE
    case 192: KB_CHAR('1', '!'); break; // (xc0): KEY_TR_1
    case 193: KB_CHAR('q', 'Q'); break; // (xc1): KEY_Q
    case 194: KB_CHARC('a', 'A', '\x01'); break; // (xc2): KEY_A
    case 195: KB_CHAR('z', 'Z'); break; // (xc3): KEY_Z
    case 197: KB_CHAR('2', '@'); break; // (xc5): KEY_TR_2
    case 198: KB_CHAR('w', 'W'); break; // (xc6): KEY_W
    case 199: KB_CHAR('s', 'S'); break; // (xc7): KEY_S
    case 200: KB_CHAR('x', 'X'); break; // (xc8): KEY_X
    case 201: KB_CHAR('<', '>'); break; // (xc9): KEY_LANGLE_RANGLE
    case 203: KB_CHAR('3', '#'); break; // (xcb): KEY_TR_3
    case 204: KB_CHARC('e', 'E', '\x05'); break; // (xcc): KEY_E
    case 205: KB_CHARC('d', 'D', '\x04'); break; // (xcd): KEY_D
    case 206: KB_CHARC('c', 'C', '\x03'); break; // (xce): KEY_C
    case 208: KB_CHAR('4', '$'); break; // (xd0): KEY_TR_4
    case 209: KB_CHAR('r', 'R'); break; // (xd1): KEY_R
    case 210: KB_CHARC('f', 'F', '\x06'); break; // (xd2): KEY_F
    case 211: KB_CHAR('v', 'V'); break; // (xd3): KEY_V
    case 212: KB_CHAR(' ', ' '); break; // (xd4): KEY_SPACE
    case 214: KB_CHAR('5', '%'); break; // (xd6): KEY_TR_5
    case 215: KB_CHAR('t', 'T'); break; // (xd7): KEY_T
    case 216: KB_CHARC('g', 'G', '\x07'); break; // (xd8): KEY_G
    case 217: KB_CHARC('b', 'B', '\x02'); break; // (xd9): KEY_B
    case 219: KB_CHAR('6', '^'); break; // (xdb): KEY_TR_6
    case 220: KB_CHAR('y', 'Y'); break; // (xdc): KEY_Y
    case 221: KB_CHARC('h', 'H', '\x08'); break; // (xdd): KEY_H
    case 222: KB_CHAR('n', 'N'); break; // (xde): KEY_N
    case 224: KB_CHAR('7', '&'); break; // (xe0): KEY_TR_7
    case 225: KB_CHAR('u', 'U'); break; // (xe1): KEY_U
    case 226: KB_CHARC('j', 'J', '\x0A'); break; // (xe2): KEY_J
    case 227: KB_CHAR('m', 'M'); break; // (xe3): KEY_M
    case 229: KB_CHAR('8', '*'); break; // (xe5): KEY_TR_8
    case 230: KB_CHARC('i', 'I', '\x09'); break; // (xe6): KEY_I
    case 231: KB_CHARC('k', 'K', '\x0B'); break; // (xe7): KEY_K
    case 232: KB_CHAR(',', '<'); break; // (xe8): KEY_COMMA
    case 234: KB_CHAR('9', '('); break; // (xea): KEY_TR_9
    case 235: KB_CHAR('o', 'O'); break; // (xeb): KEY_O
    case 236: KB_CHARC('l', 'L', '\x0C'); break; // (xec): KEY_L
    case 237: KB_CHAR('.', '>'); break; // (xed): KEY_PERIOD
    case 239: KB_CHAR('0', ')'); break; // (xef): KEY_TR_0
    case 240: KB_CHAR('p', 'P'); break; // (xf0): KEY_P
    case 242: KB_CHAR(';', ':'); break; // (xf2): KEY_SEMICOLON
    case 243: KB_CHAR('/', '?'); break; // (xf3): KEY_QMARK
    case 245: KB_CHAR('=', '+'); break; // (xf5): KEY_PLUS
    case 246: KB_CHAR(']', '}'); break; // (xf6): KEY_RBRACE
    case 247: KB_CHAR('\\', '|'); break; // (xf7): KEY_VBAR
    case 249: KB_CHAR('-', '_'); break; // (xf9): KEY_UBAR
    case 250: KB_CHAR('[', '{'); break; // (xfa): KEY_LBRACE
    case 251: KB_CHAR('\'', '"'); break; // (xfb): KEY_QUOTE
    /* Key Division 2: 145 - 165 */
    /*146 (x92): KEY_KP_0
148 (x94): KEY_KP_PERIOD
149 (x95): KEY_KP_ENTER
150 (x96): KEY_KP_1
151 (x97): KEY_KP_2
152 (x98): KEY_KP_3
153 (x99): KEY_KP_4
154 (x9a): KEY_KP_5
155 (x9b): KEY_KP_6
156 (x9c): KEY_KP_COMMA
157 (x9d): KEY_KP_7
158 (x9e): KEY_KP_8
159 (x9f): KEY_KP_9
160 (xa0): KEY_KP_HYPHEN
161 (xa1): KEY_KP_PF1
162 (xa2): KEY_KP_PF2
163 (xa3): KEY_KP_PF3
164 (xa4): KEY_KP_PF4*/
    /* Key Division 3: 188 - 188 */
    case 188: c = 0177; Serial.print("<DELETE>"); break; // (xbc): KEY_DELETE
    /* Key Division 4: 189 - 190 */
    case 189: c = '\n'; break; // (xbd): KEY_RETURN
    case 190: c = '\t'; break; // (xbe): KEY_TAB
    /* Key Division 5: (176 - 178) */
    /*176 (xb0): KEY_LOCK
177 (xb1): KEY_META*/
    /* Key Division 6: (173 - 175) */
    case 174: mod_shift = !mod_shift; Serial.print("<SHIFT "); Serial.print(mod_shift); Serial.print(">"); break; // (xae): KEY_SHIFT
    case 175: mod_ctrl = !mod_ctrl; Serial.print("<CTRL "); Serial.print(mod_ctrl); Serial.print(">"); break; // (xaf): KEY_CTRL*/
    /* Key Division 7: (166 - 168) */
    case 167: SerialTty.print("\033[D"); Serial.print("LEFT "); break; // (xa7): KEY_LEFT
    case 168: SerialTty.print("\033[C"); Serial.print("RIGHT "); break; // (xa8): KEY_RIGHT
    /* Key Division 8: (169 - 172) */
    case 169: SerialTty.print("\033[B"); Serial.print("DOWN "); break; // (xa9): KEY_DOWN
    case 170: SerialTty.print("\033[A"); Serial.print("UP "); break; // (xaa): KEY_UP
    case 171: break; // (xab): KEY_R_SHIFT
    case 100: Serial.print("<BELL ON> "); SerialKbd.print((char)LK_BELL_ENABLE); SerialKbd.print((char)LK_PARAM_VOLUME(0x7)); break;
    case 101: Serial.print("<BELL OFF> "); SerialKbd.print((char)LK_BELL_DISABLE); break;
    case 102: break; // F8
    case 103: Serial.print("<LED ON WAIT> "); SerialKbd.print((char)LK_LED_ENABLE); SerialKbd.print((char)LED_WAIT); break;
    case 104: Serial.print("<LED OFF WAIT> "); SerialKbd.print((char)LK_LED_DISABLE); SerialKbd.print((char)LED_WAIT); break;
    case 86: SerialTty.print("\x1B"); Serial.print("<ESC> "); break;

    case LK_INPUT_ERROR: Serial.print("<I-ERROR> "); break;
    case LK_ALLUP:
      mod_shift = false;
      mod_ctrl = false;
      Serial.print("<ALLUP>");
      break;
    default:
      Serial.print(key); Serial.print(' ');
  }
  if (c != -1) {
    Serial.print((char) c);
    SerialTty.print((char) c);
  }
}
