// USB-Keyboard to UART converter
// USB host interface built from Harmony
// Translation done to UART (custom library)

//needed for user code
#include <xc.h>

#include <stddef.h>                     // Defines NULL
#include <stdbool.h>                    // Defines true
#include <stdlib.h>                     // Defines EXIT_FAILURE
#include "system/common/sys_module.h"   // SYS function prototypes

//user lib for uart
#include "uart.h"
//IO api
#include "io.h"
//Saving settings to flash
#include "flash.h"
//For settings
#include "settings.h"
extern uartMode currentUartMode,newUartMode;
extern uint32_t currentBaudRate,newBaudRate;
extern uartLE currentUartLE,newUartLE;

//these need to be included to access USB API
//#include "usb/usb_host_hid_keyboard.h"
#include "usb/src/usb_host_hid_keyboard_local.h"
//#include "system/debug/sys_debug.h"
//#include "system/tmr/sys_tmr.h"
//#include "usb/usb_host_hid.h"
#define REPORTKEYCOUNT 6
#define CORE_TIMER_FREQ (24000000L)

void APP_HOST_Keyboard_EventHandler(USB_HOST_HID_KEYBOARD_HANDLE handle, USB_HOST_HID_KEYBOARD_EVENT event, void * pData); //keyboard event
int getAscii(USB_HID_KEYBOARD_KEYPAD k, USB_HID_KEYBOARD_MODIFIER_KEYS_DATA m, char lockKeys);      //convert keycode to ascii
int getExtAscii(USB_HID_KEYBOARD_KEYPAD k, USB_HID_KEYBOARD_MODIFIER_KEYS_DATA m, char lockKeys);   //convert keycode to extended ascii
void sendVT100(USB_HID_KEYBOARD_KEYPAD k, USB_HID_KEYBOARD_MODIFIER_KEYS_DATA m, char lockKeys);    //convert keycode to VT100 and send
void amberFlicker();        //turn on amber LED, set timer for later turn off
uint32_t flickerTime;       //time when LED was turned on

int main ( void ){
    int jp4last;                    //check if JP4 changes
    SYS_Initialize ( NULL );        //Harmony code- setup
    IOInit();
    RED_LED=1;                      //turn on RED LED
    //defaultSettings();
    loadSettings();
    currentBaudRate=newBaudRate;
    currentUartMode=newUartMode;
    currentUartLE=newUartLE;         
    USB_HOST_BusEnable(0);          //need to do this too    
    uart_init(currentBaudRate);     //start UART
    //uart_print("Start\r\n");        //for debugging
    USB_HOST_HID_KEYBOARD_EventHandlerSet(APP_HOST_Keyboard_EventHandler);      //called on keyboard event
    jp4last=JP1_4;
    
    while ( true ){
        int f=0;                           //check if any setup flags are set
        SYS_Tasks();                       //Harmony code- USB servicing etc
        if(JP1_1==0){f=1;}                 //jumper set for setup
        while(uart_available()){           //purge buffer, so no other keys get in setup
            if(uart_receive()=='~'){f=1;}     //set flag to do setup
        }
        if(JP1_3!=0){
            if((JP1_4==0)&&(jp4last!=0)){       //jp4 has been set
                defaultSettings();
                if(currentBaudRate!=newBaudRate){     //change baud rate if necessary
                    uart_init(newBaudRate);
                }
                currentBaudRate=newBaudRate;
                currentUartMode=newUartMode;
                currentUartLE=newUartLE;                                 
                RAMImage[0]=newUartMode;
                RAMImage[1]=newUartLE;
                RAMImage[2]=newBaudRate;
                saveToFlash();          //don't use sub as it outputs to serial port
            }
            jp4last=JP1_4;
            if(f){doSetupMenu();}   //do setup unless JP1-3 set
            if(JP1_2==0){                       //force defaults on JP2
                defaultSettings();
                if(currentBaudRate!=newBaudRate){     //change baud rate if necessary
                    uart_init(newBaudRate);
                }
                currentBaudRate=newBaudRate;
                currentUartMode=newUartMode;
                currentUartLE=newUartLE;                     
            }
        }
        if((_CP0_GET_COUNT()-flickerTime)>(CORE_TIMER_FREQ/20)){AMBER_LED=0;}       //LED off after brief delay
    }
}

void APP_HOST_Keyboard_EventHandler(USB_HOST_HID_KEYBOARD_HANDLE handle, USB_HOST_HID_KEYBOARD_EVENT event, void * pData){    
    char i,j;
    int rawAscii;
    static char lockKeys=0;
    static USB_HID_KEYBOARD_MODIFIER_KEYS_DATA lastm={0,0,0,0,0,0,0,0};     //assume no keys pressed
    USB_HID_KEYBOARD_KEYPAD key=USB_HID_KEYBOARD_KEYPAD_RESERVED_NO_EVENT_INDICATED;
    static USB_HOST_HID_KEYBOARD_DATA lastKeyData;      //for comparing states
    USB_HOST_HID_KEYBOARD_DATA* keyData=pData;    
    switch (event){
        case USB_HOST_HID_KEYBOARD_EVENT_ATTACH:
            lockKeys=0;             //all off
            RED_LED=0;                      //turn off RED LED
            GREEN_LED=1;                    //turn on GREEN LED
            break;
        case USB_HOST_HID_KEYBOARD_EVENT_DETACH:
            RED_LED=1;                      //turn on RED LED
            GREEN_LED=0;                    //turn off GREEN LED
            break;            
        case USB_HOST_HID_KEYBOARD_EVENT_REPORT_RECEIVED:
            for(i=0;i<REPORTKEYCOUNT;i++){
                key=keyData->nonModifierKeysData[i].keyCode;
                if(keyData->nonModifierKeysData[i].event==USB_HID_KEY_PRESSED){
                    for(j=0;j<REPORTKEYCOUNT;j++){
                        //if key was pressed last report, don't output again
                        if((lastKeyData.nonModifierKeysData[j].keyCode==key)&&(lastKeyData.nonModifierKeysData[j].event==USB_HID_KEY_PRESSED)){
                            key=USB_HID_KEYBOARD_KEYPAD_RESERVED_NO_EVENT_INDICATED;
                        }
                    }
                    if(key==83){lockKeys=lockKeys^1;USB_HOST_HID_KEYBOARD_ReportSend(handle,lockKeys);}       //toggle num lock
                    if(key==57){lockKeys=lockKeys^2;USB_HOST_HID_KEYBOARD_ReportSend(handle,lockKeys);}       //toggle caps lock
                    if(key==71){lockKeys=lockKeys^4;USB_HOST_HID_KEYBOARD_ReportSend(handle,lockKeys);}       //toggle scroll lock
                    if(key!=USB_HID_KEYBOARD_KEYPAD_RESERVED_NO_EVENT_INDICATED){
                        //uart_printi(key);           //for debugging
                        rawAscii=getAscii(key,keyData->modifierKeysData,lockKeys);
                        if((rawAscii>=0)&&key){
                            uart_send(rawAscii);
                            amberFlicker();
                        }else{                                                  //handle non-simple-ascii cases
                            if((key==88)||(key==40)){                           //enter numpad and normal
                                if(currentUartLE&UART_LE_CRONLY){uart_send(13);}
                                if(currentUartLE&UART_LE_LFONLY){uart_send(10);}
                                amberFlicker();                                
                            }
                            if(currentUartMode==UART_MODE_ASCII_EXT){           //add extended ascii codes in extended mode
                                rawAscii=getExtAscii(key,keyData->modifierKeysData,lockKeys);
                                if((rawAscii>=0)&&key){
                                    uart_send(rawAscii);amberFlicker();
                                }else{
                                    //various other misc keys
                                    if(key==83){if(lockKeys&1){uart_send( 0xE4);}else{uart_send( 0xE5);}amberFlicker();}            //send num lock change
                                    if(key==57){if(lockKeys&2){uart_send( 0xE0);}else{uart_send( 0xE1);}amberFlicker();}            //send caps lock change
                                    if(key==71){if(lockKeys&4){uart_send( 0xE2);}else{uart_send( 0xE3);}amberFlicker();}            //send scroll lock change
                                }
                            }else if(currentUartMode==UART_MODE_VT100){             //add VT100 sequences in VT100 mode
                                sendVT100(key,keyData->modifierKeysData,lockKeys);  //outputting strings, so do in function
                            }
                        }
                    }
                }
                //mod keys- may not have a key event associated (obviously, shift is not mapped)
                //see https://www.arduino.cc/reference/en/language/functions/usb/keyboard/keyboardmodifiers/ for mapping used
                if(currentUartMode==UART_MODE_ASCII_EXT){                                                     //meaningless in VT100 mode
                    //key down
                    if((!lastm.leftControl)&&(keyData->modifierKeysData.leftControl)){uart_send(128);amberFlicker();}        //leftControl depressed
                    if((!lastm.leftShift)&&(keyData->modifierKeysData.leftShift)){uart_send(129);amberFlicker();}            //leftShift depressed
                    if((!lastm.leftAlt)&&(keyData->modifierKeysData.leftAlt)){uart_send(130);amberFlicker();}                //leftAlt depressed
                    if((!lastm.leftGui)&&(keyData->modifierKeysData.leftGui)){uart_send(131);amberFlicker();}                //leftGui depressed
                    if((!lastm.rightControl)&&(keyData->modifierKeysData.rightControl)){uart_send(132);amberFlicker();}      //rightControl depressed
                    if((!lastm.rightShift)&&(keyData->modifierKeysData.rightShift)){uart_send(133);amberFlicker();}          //rightShift depressed
                    if((!lastm.rightAlt)&&(keyData->modifierKeysData.rightAlt)){uart_send(134);amberFlicker();}              //rightAlt depressed
                    if((!lastm.rightGui)&&(keyData->modifierKeysData.rightGui)){uart_send(135);amberFlicker();}              //rightGui depressed
                    //key up
                    if((lastm.leftControl)&&(!keyData->modifierKeysData.leftControl)){uart_send(144);amberFlicker();}        //leftControl depressed
                    if((lastm.leftShift)&&(!keyData->modifierKeysData.leftShift)){uart_send(145);amberFlicker();}            //leftShift depressed
                    if((lastm.leftAlt)&&(!keyData->modifierKeysData.leftAlt)){uart_send(146);amberFlicker();}                //leftAlt depressed
                    if((lastm.leftGui)&&(!keyData->modifierKeysData.leftGui)){uart_send(147);amberFlicker();}                //leftGui depressed
                    if((lastm.rightControl)&&(!keyData->modifierKeysData.rightControl)){uart_send(148);amberFlicker();}      //rightControl depressed
                    if((lastm.rightShift)&&(!keyData->modifierKeysData.rightShift)){uart_send(149);amberFlicker();}          //rightShift depressed
                    if((lastm.rightAlt)&&(!keyData->modifierKeysData.rightAlt)){uart_send(150);amberFlicker();}              //rightAlt depressed
                    if((lastm.rightGui)&&(!keyData->modifierKeysData.rightGui)){uart_send(151);amberFlicker();}              //rightGui depressed
                }
                lastm=keyData->modifierKeysData;                                                              //save m
            }
            memcpy(&lastKeyData, keyData, sizeof(lastKeyData));                 //copy to compare next report
            break;
    }
}

int getAscii(USB_HID_KEYBOARD_KEYPAD k, USB_HID_KEYBOARD_MODIFIER_KEYS_DATA m, char lockKeys){
    //we only need to worry about Caps lock, number lock, shift. Scroll lock, control, alt, gui don't change the ascii codes
    char caps=0,shift=0,num=0;
    //set mod bits
    if(lockKeys&1){num=1;}                  //num lock is in
    if(lockKeys&2){caps=1;}                 //caps lock is in
    if(m.leftShift||m.rightShift){shift=1;} //either shift pressed
    //key decoding
    if((currentUartMode==UART_MODE_VT100)&&(m.leftControl||m.rightControl)){//catch CTRL-x combos for VT100 mode
        if((k>3)&&(k<30)){
            return k-3;                     //CTRL-A..CTRL-Z => 1..26
        }
    }
    //alphas (modified by shift and caps)
    if((k>3)&&(k<30)){
        if(shift^caps){                     //either shift or caps
            return 61+k;
        }else{
            return 93+k;                    //both shift and caps or neither
        }
    }
    //other keys that may be shifted
    if((k>29)&&(k<83)){
        if(shift){
            switch(k){
                case 30: return '!';break;
                case 31: return '@';break;
                case 32: return '#';break;
                case 33: return '$';break;
                case 34: return '%';break;
                case 35: return '^';break;
                case 36: return '&';break;
                case 37: return '*';break;
                case 38: return '(';break;
                case 39: return ')';break;
                case 44: return ' ';break;      //isn't shifted, but handle it anyway
                case 45: return '_';break;
                case 46: return '+';break;
                case 47: return '{';break;
                case 48: return '}';break;
                case 49: return '|';break;
                case 51: return ':';break;
                case 52: return '"';break;
                case 53: return '~';break;
                case 54: return '<';break;
                case 55: return '>';break;
                case 56: return '?';break;
            }
        }else{
            switch(k){
                case 30: return '1';break;
                case 31: return '2';break;
                case 32: return '3';break;
                case 33: return '4';break;
                case 34: return '5';break;
                case 35: return '6';break;
                case 36: return '7';break;
                case 37: return '8';break;
                case 38: return '9';break;
                case 39: return '0';break;
                case 44: return ' ';break;
                case 45: return '-';break;
                case 46: return '=';break;
                case 47: return '[';break;
                case 48: return ']';break;
                case 49: return '\\';break;
                case 51: return ';';break;
                case 52: return '\'';break;
                case 53: return '`';break;
                case 54: return ',';break;
                case 55: return '.';break;
                case 56: return '/';break;
            }       
        }        
    }
    //number pad
    if((k>83)&&(k<100)){
        if(num){
            switch(k){
                case 84: return '/';break;
                case 85: return '*';break;
                case 86: return '-';break;
                case 87: return '+';break;
                case 89: return '1';break;
                case 90: return '2';break;
                case 91: return '3';break;
                case 92: return '4';break;
                case 93: return '5';break;
                case 94: return '6';break;
                case 95: return '7';break;
                case 96: return '8';break;
                case 97: return '9';break;
                case 98: return '0';break;
                case 99: return '.';break;
            }
        }else{
            //most map to arrow keys etc- only handled in extended modes
            switch(k){
                case 84: return '/';break;
                case 85: return '*';break;
                case 86: return '-';break;
                case 87: return '+';break;
            }
        }
    }
    //other special cases
    if(k==41){return 27;}     //escape
    if(k==42){return 8;}      //backspace
    if(k==43){return 9;}      //tab
    //otherwise, no output
    return -1;
}

int getExtAscii(USB_HID_KEYBOARD_KEYPAD k, USB_HID_KEYBOARD_MODIFIER_KEYS_DATA m, char lockKeys){  //convert keycode to extended ascii
    //see https://www.arduino.cc/reference/en/language/functions/usb/keyboard/keyboardmodifiers/ for mapping used
    //alt, ctrl and gui handled by inline code (to avoid keyevent being validated)
    char num=0;
    //set mod bits
    if(lockKeys&1){num=1;}                  //num lock is in
    //F1-F12
    if((k>57)&&(k<70)){
        return k+194-58;        //F1 > keycode 58 returns 194
    }
    //arrow keys, home, end pgup, pgdown, insert, delete
    if((k>72)&&(k<83)){
        return k+209-73;        //INSERT > keycode 73 returns 209
    }
    if(!num){                   //number pad cursor keys
        switch(k){
            case 89: return 213;        //end
            case 90: return 217;        //down arrow
            case 91: return 214;        //page down
            case 92: return 216;        //left arrow
            case 94: return 215;        //right arrow
            case 95: return 210;        //home
            case 96: return 218;        //up arrow
            case 97: return 211;        //page up
            case 98: return 209;        //insert
            case 99: return 212;        //delete            
        }
    }
    if(k==70){return 0xE6;}             //print screen
    if(k==72){return 0xE7;}             //pause
    return -1;
}

void sendVT100(USB_HID_KEYBOARD_KEYPAD k, USB_HID_KEYBOARD_MODIFIER_KEYS_DATA m, char lockKeys){  //convert keycode to VT100 and send
    char num=0;
    //set mod bits
    if(lockKeys&1){num=1;}                  //num lock is in
    //F1-F12
    switch(k){
        case 58: uart_print("\x1B[11~");amberFlicker();break;      //F1
        case 59: uart_print("\x1B[12~");amberFlicker();break;      //F2
        case 60: uart_print("\x1B[13~");amberFlicker();break;      //F3
        case 61: uart_print("\x1B[14~");amberFlicker();break;      //F4
        case 62: uart_print("\x1B[15~");amberFlicker();break;      //F5
        case 63: uart_print("\x1B[17~");amberFlicker();break;      //F6   -16 skipped!
        case 64: uart_print("\x1B[18~");amberFlicker();break;      //F7
        case 65: uart_print("\x1B[19~");amberFlicker();break;      //F8
        case 66: uart_print("\x1B[20~");amberFlicker();break;      //F9
        case 67: uart_print("\x1B[21~");amberFlicker();break;      //F10
        case 68: uart_print("\x1B[23~");amberFlicker();break;      //F11   -22 skipped!
        case 69: uart_print("\x1B[24~");amberFlicker();break;      //F12
        case 73: uart_print("\x1B[2~");amberFlicker();break;       //INSERT                                  
        case 74: uart_print("\x1B[1~");amberFlicker();break;       //HOME                                    
        case 75: uart_print("\x1B[5~");amberFlicker();break;       //PAGE_UP                                 
        case 76: uart_print("\x1B[3~");amberFlicker();break;       //DELETE
        case 77: uart_print("\x1B[4~");amberFlicker();break;       //END                                     
        case 78: uart_print("\x1B[6~");amberFlicker();break;       //PAGE_DOWN                               
        case 79: uart_print("\x1B[C");amberFlicker();break;        //RIGHT_ARROW                             
        case 80: uart_print("\x1B[D");amberFlicker();break;        //LEFT_ARROW                              
        case 81: uart_print("\x1B[B");amberFlicker();break;        //DOWN_ARROW                              
        case 82: uart_print("\x1B[A");amberFlicker();break;        //UP_ARROW                                        
    }
    if(!num){                   //number pad cursor keys
        switch(k){
            case 89: uart_print("\x1B[4~");amberFlicker();break;     //1_AND_END                                 
            case 90: uart_print("\x1B[B");amberFlicker();break;      //2_AND_DOWN_ARROW                          
            case 91: uart_print("\x1B[6~");amberFlicker();break;     //3_AND_PAGE_DOWN                           
            case 92: uart_print("\x1B[D");amberFlicker();break;      //4_AND_LEFT_ARROW   (no alternate on numpad 5)                       
            case 94: uart_print("\x1B[C");amberFlicker();break;      //6_AND_RIGHT_ARROW                         
            case 95: uart_print("\x1B[1~");amberFlicker();break;     //7_AND_HOME                                
            case 96: uart_print("\x1B[A");amberFlicker();break;      //8_AND_UP_ARROW                            
            case 97: uart_print("\x1B[5~");amberFlicker();break;     //9_AND_PAGE_UP                             
            case 98: uart_print("\x1B[2~");amberFlicker();break;     //0_AND_INSERT                              
            case 99: uart_print("\x1B[3~");amberFlicker();break;     //PERIOD_AND_DELETE                         
        }
    }
}

void amberFlicker(){        //turn on amber LED, set timer for later turn off
    AMBER_LED=1;
    flickerTime=_CP0_GET_COUNT();
}