
#include <Arduino.h>
#include "abc800_keyboard.h"
#include "abc800_gpio.h"
#include "abc800_config.h"
#include "WiFi.h"
#include <SoftwareSerial.h>
static const char TAG[] = __FILE__;
extern QueueHandle_t abc800_key_queue_out;
extern QueueHandle_t abc800_key_queue_in;
extern TaskHandle_t Keyboard_Task_Out;
extern TaskHandle_t Keyboard_ABC800_Task_In;
extern abc800::gpio abc800_gpio;
extern abc800::keyboard abc800_keyboard;
extern TimerHandle_t abc800_repeat_delay_timer;
extern TimerHandle_t abc800_repeat_timer;
extern TimerHandle_t abc800_check_timer;
extern SoftwareSerial abc800_serial_keyboard;
void  onRepeatTimer_abc800_in(TimerHandle_t xTimer) {
  abc800_keyboard.repeat_abc800_key();
  xTimerStart(abc800_repeat_timer, portMAX_DELAY);
}
void  onCheckTimer_abc800_in(TimerHandle_t xTimer) {
  abc800_keyboard.check_abc800_key();
}



namespace abc800 
{
   	void KeyboardTaskOut(void * parameter)
    {
        key800queue key;
        while (1) 
        {
            // See if there's a message in the queue (do not block)        
            if (xQueueReceive(abc800_key_queue_out, (void *)&key, portMAX_DELAY) == pdTRUE) {
                uint8_t special_key;
                if (special_key=abc800_keyboard.specialABC800Keycode(key)){
                    abc800_gpio.sendkey(special_key);
                }else{
                    abc800_gpio.sendkey(abc800_keyboard.getABC800Keycode(key)); 
                }
                vTaskDelay((key.ascii == 0x13 ? 100 : 0) / portTICK_PERIOD_MS);
            }
        }
    }

    void KeyboardABC800TaskIn(void * parameter)
    {
        key800queue key;
        while (1) 
        {
            // See if there's a message in the queue (do not block) 
            uint32_t keyboard_action = 0;
            if (xTaskNotifyWait(0, ULONG_MAX, &keyboard_action , 5)  == pdPASS) {


                if (keyboard_action == ABC800_KEY_RESET)
                {
                    ESP_LOGI(TAG,"RESET");
                    digitalWrite(A800_RST,LOW);
                    delay(100);
                    digitalWrite(A800_RST,HIGH);

                }

                if (keyboard_action == ABC800_KEY_KD)
                {
                    ESP_LOGI(TAG,"KD INT");
                    xTimerStop(abc800_repeat_delay_timer, portMAX_DELAY);
                    xTimerStop(abc800_repeat_timer, portMAX_DELAY);
                    xTimerStop(abc800_check_timer, portMAX_DELAY);

                }

            } 
            if (Serial1.available())
            {
                int code = Serial1.read();
                ESP_LOGI(TAG,"BACK from computer %i",code);
                abc800_serial_keyboard.write(code);
            }
                if (abc800_serial_keyboard.available() > 0)
                {
                    ESP_LOGI(TAG,"KEY IN");
                    if (abc800_gpio.abc800_getupdown()){
                        uint8_t key = abc800_serial_keyboard.read();
                        key800queue ascii = abc800_keyboard.getKeyboardKeycode(key);
                        abc800_keyboard.abc800_old_key = key;
                        if (xQueueSend(abc800_key_queue_out, (void *)&ascii, 0) != pdTRUE) {
                            ESP_LOGE(TAG,"Queue full");
                        }
                        /*
                        xTimerStop(abc800_repeat_delay_timer, portMAX_DELAY);
                        xTimerStop(abc800_repeat_timer, portMAX_DELAY);
                        xTimerStop(abc800_check_timer, portMAX_DELAY);
                        xTimerStart(abc800_repeat_delay_timer, portMAX_DELAY);
                        xTimerStart(abc800_check_timer, portMAX_DELAY);
                        */
                    }else{
                        /*
                        xTimerStop(abc800_repeat_delay_timer, portMAX_DELAY);
                        xTimerStop(abc800_repeat_timer, portMAX_DELAY);
                        xTimerStop(abc800_check_timer, portMAX_DELAY);
                        */
                    }
                } 
            vTaskDelay(1 / portTICK_PERIOD_MS);    
        }
    }
    
    uint8_t keyboard::repeat_abc800_key (){
        uint8_t key = abc800_gpio.abc800_getkey();
        key800queue key800 = abc800_keyboard.getKeyboardKeycode(key);
        if (xQueueSend(abc800_key_queue_out, (void *)&key800, 0) != pdTRUE) {
            ESP_LOGE(TAG,"Queue full");
        }
        return 0;
    }
    uint8_t keyboard::check_abc800_key (){
        uint8_t key = abc800_gpio.abc800_getkey();
        if (key!=abc800_keyboard.abc800_old_key && abc800_gpio.abc800_getupdown()){
            key800queue key800 = abc800_keyboard.getKeyboardKeycode(key);
            if (xQueueSend(abc800_key_queue_out, (void *)&key800, 0) != pdTRUE) {
                ESP_LOGE(TAG,"Queue full");
            }
            abc800_keyboard.abc800_old_key=key;
        }
        return 0;
    }
    void keyboard::init()
    {
        abc800_repeat_delay_timer = xTimerCreate(
                      "ABC800 Repeat Delay Timer",           // Name of timer
                      ABC800_KEY_REPEAT_TIME_DELAY/ portTICK_PERIOD_MS,  // Period of timer (in ticks)
                      pdFALSE,                    // Auto-reload
                      (void *)2,                  // Timer ID
                      onRepeatTimer_abc800_in);           // Callback function

        abc800_repeat_timer = xTimerCreate(
                      "ABC800 Repeat Timer",        // Name of timer
                      ABC800_KEY_REPEAT_TIME / portTICK_PERIOD_MS,  // Period of timer (in ticks)
                      pdFALSE,                     // Auto-reload
                      (void *)3,                  // Timer ID
                      onRepeatTimer_abc800_in);    // Callback function
        abc800_check_timer = xTimerCreate(
                      "ABC800 Check Timer",        // Name of timer
                      ABC800_KEY_CHECK_TIME / portTICK_PERIOD_MS,  // Period of timer (in ticks)
                      pdFALSE,                     // Auto-reload
                      (void *)4,                  // Timer ID
                      onCheckTimer_abc800_in);    // Callback function

       
        xTaskCreatePinnedToCore(
        KeyboardTaskOut,
        "Keyboard_Task_Out",
        2048,
        NULL,
        4,
        &Keyboard_Task_Out,
        1);

        xTaskCreatePinnedToCore(
        KeyboardABC800TaskIn,
        "Keyboard_Task_ABC800_In",
        2048,
        NULL,
        2,
        &Keyboard_ABC800_Task_In,
        1);

    }
    uint8_t keyboard::specialABC800Keycode(key800queue keyqueue){
        // Arrow up
        if (keyqueue.special == 0x52)
        {
            return (0x17);
        }
        // Arrow down
        if (keyqueue.special == 0x51)
        {
            return (0x1a);
        }
        // Forward
        if (keyqueue.special == 0x4f){
            return(0x09);
        }
        // Backwards
        if ( keyqueue.special == 0x50 ){
            return(0x08);
        }
        //F1-F8
        if (keyqueue.special >= 0x3a && keyqueue.special <= 0x41 && keyqueue.modifier == 0x00)
        {

            return (keyqueue.special+0x86);
        }
        //F1-F8 Shift
        if (keyqueue.special >= 0x3a && keyqueue.special <= 0x41 && (keyqueue.modifier == 0x02 ||  keyqueue.modifier == 0x22))
        {

            return (keyqueue.special+0x96);
        }
        //F1-F8 CTRL
        if (keyqueue.special >= 0x3a && keyqueue.special <= 0x41 && (keyqueue.modifier == 0x01))
        {

            return (keyqueue.special+0xA6);
        }
        //F1-F8 CTRL+SHIFT
        if (keyqueue.special >= 0x3a && keyqueue.special <= 0x41 && (keyqueue.modifier == 0x03 ||  keyqueue.modifier == 0x33))
        {

            return (keyqueue.special+0xB6);
        }
        if (keyqueue.special == 0x42 && (keyqueue.modifier == 0x06 ||  keyqueue.modifier == 0x36)) //F9 + alt +shift
        {
            pinMode(KEY800_MENU_BUTTON,OUTPUT);
            digitalWrite(KEY800_MENU_BUTTON,LOW);
            delay(100);
            pinMode(KEY800_MENU_BUTTON,INPUT);
        }
        if (keyqueue.special == 0x43 && (keyqueue.modifier == 0x06 ||  keyqueue.modifier == 0x36)) //F10 + alt +shift
        {
            pinMode(KEY800_UP_BUTTON,OUTPUT);
            digitalWrite(KEY800_UP_BUTTON,LOW);
            delay(100);
            pinMode(KEY800_UP_BUTTON,INPUT);
        }
        if (keyqueue.special == 0x44 && (keyqueue.modifier == 0x06 ||  keyqueue.modifier == 0x36)) //F11 + alt +shift
        {
            pinMode(KEY800_DOWN_BUTTON,OUTPUT);
            digitalWrite(KEY800_DOWN_BUTTON,LOW);
            delay(100);
            pinMode(KEY800_DOWN_BUTTON,INPUT);
        }
        if (keyqueue.special == 0x45 && (keyqueue.modifier == 0x06 ||  keyqueue.modifier == 0x36)) //F12 + alt +shift
        {
            char * x= new char[50];
            String IP ="rem IP-Address: "+WiFi.localIP().toString();
            uint8_t len = IP.length();
            
            IP.toCharArray(x, len+1);
            key800queue key800queue ;
            key800queue.modifier=0x00;
            key800queue.special=0x00;
            for (uint8_t i=0;i<=(len);i++){
                key800queue.ascii = x[i];
                if (xQueueSend(abc800_key_queue_out, (void *)&key800queue, 0) != pdTRUE) {
                    ESP_LOGE(TAG,"Queue full");
                }
            }
            key800queue.ascii=0x13;
            key800queue.modifier=0x00;
            key800queue.special=0x00;
            if (xQueueSend(abc800_key_queue_out, (void *)&key800queue, 0) != pdTRUE) {
                    ESP_LOGE(TAG,"Queue full");
            }
            return (0x00);
        }
        return (0x00);

    }
    key800queue keyboard::getKeyboardKeycode(uint8_t abc800key){
            key800queue outkey;
            outkey.modifier =0x00;
            outkey.special = 0x00;
            outkey.ascii = abc800key;
            ESP_LOGI(TAG,"KEY %d",abc800key);
            if (abc800key >=0x61 && abc800key <=0x7a){ // small letter
                return (outkey);
            }
            if (abc800key >=0x41 && abc800key <=0x5a){ // Capital
                return (outkey);
            }
            if (abc800key >=0x30 && abc800key <=0x39){ // Digits
                return (outkey);
            }
            if (    abc800key >= 0x21 && 
                    abc800key <= 0x29 && 
                    abc800key != 0x24 && 
                    abc800key != 0x27){ // Digits symbols
                return (outkey);
            }
            
            switch(abc800key){
   
                case 0x2b:		// +
                    return (outkey);
                    break;
                case 0x27:		// '
                    return (outkey);
                    break;
                case 0x3f:		// ?
                    return (outkey);
                    break;
                case 0x3d:		// ?
                    return (outkey);
                    break;
                case 0x2d:		// -
                    return (outkey);
                    break;
                case 0x2c:		// ,
                    return (outkey);
                    break;
                case 0x2e:		// .
                    return (outkey);
                    break;
                case 0x3b:		// ;
                    return (outkey);
                    break;
                case 0x3c:		// ;
                    return (outkey);
                    break;
                case 0x3a:		// :
                    return (outkey);
                    break;
                case 0x5f:		// ?
                    return (outkey);
                    break;
                case 0x2f:		// /
                    return (outkey);
                    break;                
                 case 0x3e:      //<
                    return (outkey);
                    break;              
                case 0x20:		// SPACE INVADERS
                    return (outkey);
                    break;
                case 0x2a:		// * STAR WARS
                    return (outkey);
                    break;
                //None Match
                case 0x0d:
                    outkey.ascii = 0x13;
                    return (outkey);
                    break;
    
                case 0x40:		// É
                    outkey.ascii = 0xe9;
                    return (outkey);
                    break;
                case 0x60:		// é
                    outkey.ascii = 0xc9;
                    return (outkey);
                    break;
                case 0x5e:		// Ü
                    outkey.ascii = 0xfc;
                    return (outkey);
                    break;
                case 0x7e:		// ü
                    outkey.ascii = 0xdc;
                    return (outkey);
                    break;
                case 0x24:		// ¤
                    outkey.ascii = 0xa4;
                    return (outkey);
                    break;
                case 0x7c:      //ö
                    outkey.ascii = 0xf6;
                    return (outkey);
                    break; 
                case 0x5c:      //Ö
                    outkey.ascii = 0xd6;
                    return (outkey);
                    break;  
                case 0x7d:      //å
                    outkey.ascii = 0xe5;
                    return (outkey);
                    break; 
                case 0x5d:      //Å
                    outkey.ascii = 0xc5;
                    return (outkey);
                    break;  
                case 0x7b:      //ä
                    outkey.ascii = 0xe4;
                    return (outkey);
                    break; 
                case 0x5b:      //Ä
                    outkey.ascii = 0xc4;
                    return (outkey);
                    break;    
            }
            
            //CTRL modifier + char
            if (abc800key>=0x01 && abc800key<=0x1a){
                outkey.ascii=abc800key+0x60;
                outkey.modifier=0x01;
                return (outkey);  
            }
            switch (abc800key) {
                case 0x1f:      // O // F4 ABC82
                    outkey.modifier=0x03;
                    outkey.ascii=0x4f;
                    return (outkey);
                    break; 
                case 0x00:      // P
                    outkey.modifier=0x03;
                    outkey.ascii=0x50;
                    return (outkey);
                    break; 
                case 0x1b:      // K
                    outkey.modifier=0x03;
                    outkey.ascii=0x4b;
                    return (outkey);
                    break; 
                case 0x1c:      // L // F1 ABC82
                    outkey.modifier=0x03;
                    outkey.ascii=0x4c;
                    return (outkey);
                    break; 
                case 0x1e:      // N // F3 ABC82
                    outkey.modifier=0x03;
                    outkey.ascii=0x4e;
                    return (outkey);
                    break; 
                case 0x1d:      // M // F2ABC82
                    outkey.modifier=0x03;
                    outkey.ascii=0x4d;
                    return (outkey);
                    break; 
            }
            outkey.ascii = 0x00;
            return (outkey);

    }
    uint8_t keyboard::getABC800Keycode(key800queue keyqueue) {
        //Observe order fall through
        if (!keyqueue.modifier ||  keyqueue.modifier == 0x02 ||  keyqueue.modifier == 0x20){ // handle ascii chart with modifer for shift etc
            if (keyqueue.ascii >=0x61 && keyqueue.ascii <=0x7a){ // small letter
                return (keyqueue.ascii);
            }
            if (keyqueue.ascii >=0x41 && keyqueue.ascii <=0x5a){ // Capital
                return (keyqueue.ascii);
            }
            if (keyqueue.ascii >=0x30 && keyqueue.ascii <=0x39){ // Digits
                return (keyqueue.ascii);
            }
            if (keyqueue.ascii >=0x21 && keyqueue.ascii <=0x29 && keyqueue.ascii!=0x24 && keyqueue.ascii!=0x27){ // Digits symbols
                return (keyqueue.ascii);
            }
            
            switch (keyqueue.ascii) {
                case 0xe9:		// É
                    return 0x40;
                    break;
                case 0xc9:		// é
                    return 0x60;
                    break;
                case 0xfc:		// Ü
                    return 0x5e;
                    break;
                case 0xdc:		// ü
                    return 0x7e;
                    break;
                case 0xa4:		// ¤
                    return 0x24;
                    break;
                case 0x13:		// ENTER CYPER SPACE
                    return 0x0d;
                    break;
                case 0xf6:      //ö
                    return 0x7c;
                    break; 
                case 0xD6:      //Ö
                    return 0x5c;
                    break;  
                case 0xe5:      //å
                    return 0x7d;
                    break; 
                case 0xC5:      //Å
                    return 0x5d;
                    break;  
                case 0xe4:      //ä
                    return 0x7b;
                    break; 
                case 0xC4:      //Ä
                    return 0x5b;
                    break;    
                //From here the ABC80 matches     
                case 0x2b:		// +
                    return 0x2b;
                    break;
                case 0x27:		// '
                    return 0x27;
                    break;
                case 0x3f:		// ?
                    return 0x3f;
                    break;
                case 0x3d:		// ?
                    return 0x3d;
                    break;
                case 0x2d:		// -
                    return 0x2d;
                    break;
                case 0x2c:		// ,
                    return 0x2c;
                    break;
                case 0x2e:		// .
                    return 0x2e;
                    break;
                case 0x3b:		// ;
                    return 0x3b;
                    break;
                case 0x3c:		// ;
                    return 0x3c;
                    break;
                case 0x3a:		// :
                    return 0x3a;
                    break;
                case 0x5f:		// ?
                    return 0x5f;
                    break;
                case 0x2f:		// /
                    return 0x2f;
                    break;
                 case 0x3e:      //<
                    return 0x3e;
                    break;              
                case 0x20:		// SPACE INVADERS
                    return 0x20;
                    break;
                case 0x2a:		// * STAR WARS
                    return 0x2a;
                    break;
                case 0x4c:		// * STAR WARS
                    return 0x18;
                    break;
            }
        }

        //CTRL - C or escape
        
        if ((keyqueue.modifier == 0x01 && (keyqueue.ascii == 0x63 || keyqueue.ascii == 0x43)) || keyqueue.special == 0x29 ){
            return 0x03; 
        }
        //CTRL modifier + char
        if (keyqueue.modifier == 0x01 ){
            if (keyqueue.ascii >=0x61 && keyqueue.ascii <=0x7a){ //
                return (keyqueue.ascii-0x60);
            }   
        }
        //CTRL SHIFT modifier + char
        if (keyqueue.modifier == 0x03 || keyqueue.modifier == 0x33 ){
             switch (keyqueue.ascii) {
                case 0x4f:      // O
                    return 0x1f;
                    break; 
                case 0x50:      // P
                    return 0x00;
                    break; 
                case 0x4b:      // K
                    return 0x1b;
                    break; 
                case 0x4c:      // L
                    return 0x1c;
                    break; 
                case 0x4e:      // N
                    return 0x1e;
                    break; 
                case 0x4d:      // M
                    return 0x1d;
                    break; 
             }
        }
        // Backspace
        if (keyqueue.special == 0x2a )
            return(0x08);
        // Delete
        if (keyqueue.special == 0x4c && keyqueue.modifier!=0x05 )
            return(0x18);
        
        return (0xFF);
    }
}