/*
  keyboard.cpp

  Copyright (c) 2023, Magawin
  Original code (pre-library): Copyright (c) 2011, Peter Barrett

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "Keyboard.h"

#include "MG32x02z.h"
#include "MG32x02z_USBD_API.h"
#include "MG32x02z_USBD_KeyDescriptor.h"

#include "MG32x02z_USBD_KeyDescriptor.h"
#include "MG32x02Z_USB_KeyInit.h"

#if defined(USBCON)
//#include "usbd_hid_composite_if.h"

//================================================================================
//================================================================================
//  Keyboard

Keyboard_::Keyboard_(USB_EP_Struct*  EPX)
{
    m_Protocol = 1;
    m_EPX = EPX;
}

void USBKey_SetAPIDefault(void);
void Keyboard_::begin(void)
{
    __DRV_USB_DISCONNECT();
    delay(500);


    g_UsbEP1Ctr.Status = USBA_Status_Idle,
    g_UsbEP1Ctr.IdleRate = 0,
    g_UsbEP1Ctr.Protocol = m_Protocol;

    g_UsbEP1Ctr.mHID0_ReportLength = Key_HID0_ReportLength;
    g_UsbEP1Ctr.mHID1_ReportLength = Key_HID1_ReportLength;
    g_UsbEP1Ctr.pUSB_INTERFACE0_HID_REPORT = Key_USB_INTERFACE0_HID_REPORT;
    g_UsbEP1Ctr.pUSB_INTERFACE1_HID_REPORT = Key_USB_INTERFACE1_HID_REPORT;


    g_UsbEP1Ctr.pUSB_DEVICE_DESCRIPTOR = Key_USB_DEVICE_DESCRIPTOR;
    g_UsbEP1Ctr.pUSB_CONFIGURATION_DESCRIPTOR = Key_USB_CONFIGURATION_DESCRIPTOR;
    g_UsbEP1Ctr.pUSB_STRING_DESCRIPTOR = Key_USB_STRING_DESCRIPTOR;

    #if MF_STRING == 1                                          
    g_UsbEP1Ctr.pManufacturer_Descriptor = Key_Manufacturer_Descriptor;
    #endif      
    #if PD_STRING == 1                                          
    g_UsbEP1Ctr.pProduct_Descriptor = Key_Product_Descriptor;
    #endif   
    #if SN_STRING == 1                                          
    g_UsbEP1Ctr.pSerialNumber_Descriptor = Key_SerialNumber_Descriptor;
    #endif 
    #if MG_USB_LPM_EN == 1                                      
    g_UsbEP1Ctr.pUSB_BOS_DESCRIPTOR = Key_USB_BOS_DESCRIPTOR;
    g_UsbEP1Ctr.pUSB_EXT_DESCRIPTOR = Key_USB_EXT_DESCRIPTOR;
    #endif

    //---------------------------------------
    //USB Power Control Relationship
    UnProtectModuleReg(PWprotect);
    PW_PeriphSLEEPModeContinuous_Config(PW_SLPPO_USB,ENABLE);
    PW_PeriphSTOPModeWakeUp_Config( PW_WKSTP_USB , ENABLE);
    ProtectModuleReg(PWprotect);

    USBKey_SetAPIDefault();

    __DRV_USB_CONNECT();
    //----------------------------------------
    //USB Inital
    API_USBD_Inital();
    
    NVIC_SetPriority(USB_IRQn,  1);
}

void Keyboard_::end(void)
{
    __DRV_USB_DISCONNECT();
}




void Keyboard_::sendReport(KeyReport *keys)
{
    m_Protocol = g_UsbEP1Ctr.Protocol;

    uint8_t buf[8] = { keys->modifiers, keys->reserved, keys->keys[0], keys->keys[1],
                       keys->keys[2], keys->keys[3], keys->keys[4], keys->keys[5]
                     };
    USB_SetEndpointTXData( m_EPX, buf, 8 );

    //delay required to prevent persistent key when call print
    delay(20);
}

extern
const uint8_t _asciimap[128] PROGMEM;

#define SHIFT 0x80
const uint8_t _asciimap[128] = 
{
  0x00,             // NUL
  0x00,             // SOH
  0x00,             // STX
  0x00,             // ETX
  0x00,             // EOT
  0x00,             // ENQ
  0x00,             // ACK
  0x00,             // BEL
  0x2a,     // BS Backspace
  0x2b,     // TAB  Tab
  0x28,     // LF Enter
  0x00,             // VT
  0x00,             // FF
  0x00,             // CR
  0x00,             // SO
  0x00,             // SI
  0x00,             // DEL
  0x00,             // DC1
  0x00,             // DC2
  0x00,             // DC3
  0x00,             // DC4
  0x00,             // NAK
  0x00,             // SYN
  0x00,             // ETB
  0x00,             // CAN
  0x00,             // EM
  0x00,             // SUB
  0x00,             // ESC
  0x00,             // FS
  0x00,             // GS
  0x00,             // RS
  0x00,             // US

  0x2c,      //  ' '
  0x1e | SHIFT,  // !
  0x34 | SHIFT,  // "
  0x20 | SHIFT,  // #
  0x21 | SHIFT,  // $
  0x22 | SHIFT,  // %
  0x24 | SHIFT,  // &
  0x34,          // '
  0x26 | SHIFT,  // (
  0x27 | SHIFT,  // )
  0x25 | SHIFT,  // *
  0x2e | SHIFT,  // +
  0x36,          // ,
  0x2d,          // -
  0x37,          // .
  0x38,          // /
  0x27,          // 0
  0x1e,          // 1
  0x1f,          // 2
  0x20,          // 3
  0x21,          // 4
  0x22,          // 5
  0x23,          // 6
  0x24,          // 7
  0x25,          // 8
  0x26,          // 9
  0x33 | SHIFT,    // :
  0x33,          // ;
  0x36 | SHIFT,    // <
  0x2e,          // =
  0x37 | SHIFT,    // >
  0x38 | SHIFT,    // ?
  0x1f | SHIFT,    // @
  0x04 | SHIFT,    // A
  0x05 | SHIFT,    // B
  0x06 | SHIFT,    // C
  0x07 | SHIFT,    // D
  0x08 | SHIFT,    // E
  0x09 | SHIFT,    // F
  0x0a | SHIFT,    // G
  0x0b | SHIFT,    // H
  0x0c | SHIFT,    // I
  0x0d | SHIFT,    // J
  0x0e | SHIFT,    // K
  0x0f | SHIFT,    // L
  0x10 | SHIFT,    // M
  0x11 | SHIFT,    // N
  0x12 | SHIFT,    // O
  0x13 | SHIFT,    // P
  0x14 | SHIFT,    // Q
  0x15 | SHIFT,    // R
  0x16 | SHIFT,    // S
  0x17 | SHIFT,    // T
  0x18 | SHIFT,    // U
  0x19 | SHIFT,    // V
  0x1a | SHIFT,    // W
  0x1b | SHIFT,    // X
  0x1c | SHIFT,    // Y
  0x1d | SHIFT,    // Z
  0x2f,          // [
  0x31,          // bslash
  0x30,          // ]
  0x23 | SHIFT,  // ^
  0x2d | SHIFT,  // _
  0x35,          // `
  0x04,          // a
  0x05,          // b
  0x06,          // c
  0x07,          // d
  0x08,          // e
  0x09,          // f
  0x0a,          // g
  0x0b,          // h
  0x0c,          // i
  0x0d,          // j
  0x0e,          // k
  0x0f,          // l
  0x10,          // m
  0x11,          // n
  0x12,          // o
  0x13,          // p
  0x14,          // q
  0x15,          // r
  0x16,          // s
  0x17,          // t
  0x18,          // u
  0x19,          // v
  0x1a,          // w
  0x1b,          // x
  0x1c,          // y
  0x1d,          // z
  0x2f | SHIFT,  // {
  0x31 | SHIFT,  // |
  0x30 | SHIFT,  // }
  0x35 | SHIFT,  // ~
  0       // DEL
};


uint8_t USBPutChar(uint8_t c);

// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report.  Because of the way
// USB HID works, the host acts like the key remains pressed until we
// call release(), releaseAll(), or otherwise clear the report and resend.
size_t Keyboard_::press(uint8_t k)
{
    uint8_t i;
    if (k >= 136)
    {     
        // it's a non-printing key (not a modifier)
        k = k - 136;
    } 
    else if (k >= 128) 
    {  
        // it's a modifier key
        m_keyReport.modifiers |= (1 << (k - 128));
        k = 0;
    } 
    else 
    {
        // it's a printing key
        k = pgm_read_byte(_asciimap + k);
        if (!k) 
        {
            setWriteError();
            return 0;
        }
        if (k & 0x80) 
        {
            // it's a capital letter or other character reached with shift
            m_keyReport.modifiers |= 0x02; // the left shift modifier
            k &= 0x7F;
        }
    }

    // Add k to the key report only if it's not already present
    // and if there is an empty slot.
    if( (m_keyReport.keys[0] != k)
      &&(m_keyReport.keys[1] != k)
      &&(m_keyReport.keys[2] != k)
      &&(m_keyReport.keys[3] != k)
      &&(m_keyReport.keys[4] != k)
      &&(m_keyReport.keys[5] != k)   ) 
    {
        for (i = 0; i < 6; i++) 
        {
            if (m_keyReport.keys[i] == 0x00) 
            {
                m_keyReport.keys[i] = k;
                break;
            }
        }

        if (i == 6) 
        {
            setWriteError();
            return 0;
        }
    }
    sendReport(&m_keyReport);
    return 1;
}


// release() takes the specified key out of the persistent key report and
// sends the report.  This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t Keyboard_::release(uint8_t k)
{
    uint8_t i;
    if (k >= 136)
    {
        // it's a non-printing key (not a modifier)
        k = k - 136;
    }
    else if (k >= 128)
    { 
        // it's a modifier key
        m_keyReport.modifiers &= ~(1 << (k - 128));
        k = 0;
    }
    else
    {
        // it's a printing key
        k = pgm_read_byte(_asciimap + k);
        if (!k) 
        {
            return 0;
        }

        if (k & 0x80) 
        {            
            // it's a capital letter or other character reached with shift
            m_keyReport.modifiers &= ~(0x02);  // the left shift modifier
            k &= 0x7F;
        }
    }

    // Test the key report to see if k is present.  Clear it if it exists.
    // Check all positions in case the key is present more than once (which it shouldn't be)
    for (i = 0; i < 6; i++)
    {
        if (0 != k && m_keyReport.keys[i] == k) 
        {
            m_keyReport.keys[i] = 0x00;
        }
    }

    sendReport(&m_keyReport);
    return 1;
}

void Keyboard_::releaseAll(void)
{
    m_keyReport.keys[0] = 0;
    m_keyReport.keys[1] = 0;
    m_keyReport.keys[2] = 0;
    m_keyReport.keys[3] = 0;
    m_keyReport.keys[4] = 0;
    m_keyReport.keys[5] = 0;
    m_keyReport.modifiers = 0;
    sendReport(&m_keyReport);
}

size_t Keyboard_::write(uint8_t c)
{
    uint8_t p = press(c);  // Keydown
    release(c);            // Keyup
    return p;              // just return the result of press() since release() almost always returns 1
}

Keyboard_  Keyboard(USER_Keyboard_EPn);

#endif





