/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "usb_device_config.h"
#include "usb.h"
#include "usb_device.h"

#include "usb_device_class.h"
#include "usb_device_hid.h"

#include "usb_device_ch9.h"
#include "usb_device_descriptor.h"

#include "composite.h"

#include "hid_keyboard.h"
#include "smartdma_keyscan.h"
#include "board.h"
#include "app.h"


/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define COL_GPIO(x)  GPIO4
#if HS_HID_KEYBOARD_INTERRUPT_IN_INTERVAL == 1
//there is 8 intervals in 1ms
#define KEYBOARD_INTERVAL_MS 8
#elif HS_HID_KEYBOARD_INTERRUPT_IN_INTERVAL == 4
//there is 1 interval in 1ms
#define KEYBOARD_INTERVAL_MS 1
#endif



/*******************************************************************************
 * Prototypes
 ******************************************************************************/

static usb_status_t USB_DeviceHidKeyboardAction(void);
static usb_status_t USB_DeviceHidKeyboardAction_SmartDMA(void);

/*******************************************************************************
 * Variables
 ******************************************************************************/

USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t s_KeyboardBuffer[USB_HID_KEYBOARD_REPORT_LENGTH];
static usb_device_composite_struct_t *s_UsbDeviceComposite;
static usb_device_hid_keyboard_struct_t s_UsbDeviceHidKeyboard;

volatile uint8_t key_name[6];
volatile uint8_t key_name_temp[6];
uint8_t key_state_temp[96];
uint8_t key_state[96];
uint8_t key_count = 0;
uint8_t key_count_temp = 0;
uint16_t key_delay[96];
uint8_t key_num_temp_index = 0;

extern volatile uint32_t g_KeyValue[8];


uint16_t keyboard_map[KEY_ROW_COUNT * KEY_COL_COUNT] = {
        KEY_ESCAPE,                 KEY_NO,                 KEY_F1,      KEY_F2,            KEY_F3,       KEY_F4,        KEY_F5,      KEY_F6,          KEY_F7,             KEY_F8,             KEY_F9,              KEY_F10,                     KEY_F11,                 KEY_F12,                    KEY_DELETE,
        KEY_GRAVE_ACCENT_AND_TILDE, KEY_1_EXCLAMATION_MARK, KEY_2_AT,    KEY_3_NUMBER_SIGN, KEY_4_DOLLAR, KEY_5_PERCENT, KEY_6_CARET, KEY_7_AMPERSAND, KEY_8_ASTERISK,     KEY_9_OPARENTHESIS, KEY_0_CPARENTHESIS,  KEY_MINUS_UNDERSCORE,        KEY_EQUAL_PLUS,          KEY_BACKSPACE,              KEY_HOME,
        KEY_TAB,                    KEY_Q,                  KEY_W,       KEY_E,             KEY_R,        KEY_T,         KEY_Y,       KEY_U,           KEY_I,              KEY_O,              KEY_P,               KEY_OBRACKET_AND_OBRACE,     KEY_CBRACKET_AND_CBRACE, KEY_BACKSLASH_VERTICAL_BAR, KEY_PAGEUP,
        KEY_CAPS_LOCK,              KEY_A,                  KEY_S,       KEY_D,             KEY_F,        KEY_G,         KEY_H,       KEY_J,           KEY_K,              KEY_L,              KEY_SEMICOLON_COLON, KEY_SINGLE_AND_DOUBLE_QUOTE, KEY_ENTER,               KEY_NO,                     KEY_PAGEDOWN,
        KEY_LEFTSHIFT,              KEY_Z,                  KEY_X,       KEY_C,             KEY_V,        KEY_B,         KEY_N,       KEY_M,           KEY_COMMA_AND_LESS, KEY_DOT_GREATER,    KEY_SLASH_QUESTION,  KEY_RIGHTSHIFT,              KEY_UPARROW,             KEY_NO,                     KEY_END1,
        KEY_LEFTCONTROL,            KEY_LEFT_GUI,           KEY_LEFTALT, KEY_NO,            KEY_NO,       KEY_SPACEBAR,  KEY_NO,      KEY_NO,          KEY_RIGHTALT,       (0x5220 | 0x1F),    KEY_RIGHTCONTROL,    KEY_LEFTARROW,               KEY_DOWNARROW,           KEY_NO,                     KEY_RIGHTARROW
};



static uint8_t keyboard_col_pin[]= {
		BOARD_KEYBOARD_COL0_GPIO_PIN,
		BOARD_KEYBOARD_COL1_GPIO_PIN,
		BOARD_KEYBOARD_COL2_GPIO_PIN,
		BOARD_KEYBOARD_COL3_GPIO_PIN,
		BOARD_KEYBOARD_COL4_GPIO_PIN,
		BOARD_KEYBOARD_COL5_GPIO_PIN,
		BOARD_KEYBOARD_COL6_GPIO_PIN,
		BOARD_KEYBOARD_COL7_GPIO_PIN,
		BOARD_KEYBOARD_COL8_GPIO_PIN,
		BOARD_KEYBOARD_COL9_GPIO_PIN,
		BOARD_KEYBOARD_COL10_GPIO_PIN,
		BOARD_KEYBOARD_COL11_GPIO_PIN,
		BOARD_KEYBOARD_COL12_GPIO_PIN,
		BOARD_KEYBOARD_COL13_GPIO_PIN,
		BOARD_KEYBOARD_COL14_GPIO_PIN
};

static GPIO_Type * keyboard_col_gpio[]= {

		BOARD_KEYBOARD_COL0_GPIO,
		BOARD_KEYBOARD_COL1_GPIO,
		BOARD_KEYBOARD_COL2_GPIO,
		BOARD_KEYBOARD_COL3_GPIO,
		BOARD_KEYBOARD_COL4_GPIO,
		BOARD_KEYBOARD_COL5_GPIO,
		BOARD_KEYBOARD_COL6_GPIO,
		BOARD_KEYBOARD_COL7_GPIO,
		BOARD_KEYBOARD_COL8_GPIO,
		BOARD_KEYBOARD_COL9_GPIO,
		BOARD_KEYBOARD_COL10_GPIO,
		BOARD_KEYBOARD_COL11_GPIO,
		BOARD_KEYBOARD_COL12_GPIO,
		BOARD_KEYBOARD_COL13_GPIO,
		BOARD_KEYBOARD_COL14_GPIO
};

static uint8_t keyboard_row_pin[]= {

		BOARD_KEYBOARD_ROW0_GPIO_PIN,
		BOARD_KEYBOARD_ROW1_GPIO_PIN,
		BOARD_KEYBOARD_ROW2_GPIO_PIN,
		BOARD_KEYBOARD_ROW3_GPIO_PIN,
		BOARD_KEYBOARD_ROW4_GPIO_PIN,
		BOARD_KEYBOARD_ROW5_GPIO_PIN,

};

static GPIO_Type * keyboard_row_gpio[]= {

		BOARD_KEYBOARD_ROW0_GPIO,
		BOARD_KEYBOARD_ROW1_GPIO,
		BOARD_KEYBOARD_ROW2_GPIO,
		BOARD_KEYBOARD_ROW3_GPIO,
		BOARD_KEYBOARD_ROW4_GPIO,
		BOARD_KEYBOARD_ROW5_GPIO,
};

/*******************************************************************************
 * Code
 ******************************************************************************/

static usb_status_t USB_DeviceHidKeyboardAction(void)
{
    static int x = 0U;
    static uint32_t count = 0;
    uint8_t key_valid = 0;
    uint32_t button_lev = 1;
    uint32_t tempa = 0;
    uint32_t tempb = 0;
    uint8_t num = 0xff;
    static uint32_t tick_start = 0;
    uint32_t tick_end = 0;
    static uint8_t pre_key_num = 0xff;
    count ++;
    static uint32_t test_key_count = 0;
    GPIO_PortSet(BOARD_USB_DEBUGIO_GPIO, 1U << BOARD_USB_DEBUGIO_GPIO_PIN);

    s_UsbDeviceHidKeyboard.buffer[2] = 0x00U;
    s_UsbDeviceHidKeyboard.buffer[0] = 0x00;
    memset(s_UsbDeviceHidKeyboard.buffer,0,6);

    /* software delay ++ */
    for(int i = 0; i < key_count_temp; i++)
    {
        key_delay[key_name_temp[i]]++;
    }

    for(int i = 0; i < 15; i ++)
    {
    	/* PCOR register, set COL to 0 */
    	keyboard_col_gpio[i]->PCOR = (1 << keyboard_col_pin[i]);

        for(int j = 0; j < 6; j++)
        {
        	tempa = keyboard_row_gpio[j]->PDR[keyboard_row_pin[j]];

        	button_lev = tempa;
            num = j * 15 + i;
            /* key state change, pressed or released */
        	if ((! button_lev) != key_state_temp[num])
        	{
        		/* Pressed */
                if(! button_lev)
                {
        			//tick_start = SysTick->VAL;
        			key_state_temp[num] = 1;
        			key_name_temp[key_count_temp] = num;
					key_count_temp ++;
                }
                else   /* Released */
                {
                	key_state_temp[num] = 0;
                	for(int k = 0; k < key_count_temp; k++)
                	{
                		if(num == key_name_temp[i])
                		{
                			key_name_temp[i] = 0xFF;
                			break;
                		}
                	}
                	key_count_temp --;
                	key_delay[num] = 0;

                }
        	}
        	else /* state not change */
        	{

        	}
        }
        /* PSOR register, set COL to 1 */
        keyboard_col_gpio[i]->PSOR = (1 << keyboard_col_pin[i]);
        /* about 6us for z = 100 */
        for(int z = 0; z < 100; z++)
        {};
    }

    for(int i = 0; i < key_count_temp; i++)
    {
        if(key_state_temp[key_name_temp[i]] == 1 && key_delay[key_name_temp[i]] == 0)
        {
        	tick_end = SysTick->VAL;
        	key_state[key_name_temp[i]] = 1;
            key_name[key_count] = key_name_temp[i];
            key_count ++;
            test_key_count ++;

        }
        /* detect a long pressed event, > 200ms */
        else if (  key_state_temp[key_name_temp[i]] == 1 && key_delay[key_name_temp[i]] >= 200 * KEYBOARD_INTERVAL_MS)
        {
        	key_state[key_name_temp[i]] = 1;
        	key_name[key_count] = key_name_temp[i];
        	key_count ++;
        }

    }
    if(key_count == 0)
    {
    	s_UsbDeviceHidKeyboard.buffer[2] = 0;
    }
    else
    {
    	if(key_count > 2)
    	{
    		test_key_count ++;
    	}
		while(key_count)
		{
			key_count --;
			if(key_name[key_count] == 60 || key_name[key_count] == 71 || key_name[key_count] == 75 || key_name[key_count] == 76 || key_name[key_count] == 77 || key_name[key_count] == 83 || key_name[key_count] == 84 || key_name[key_count] == 85)
			{
				/* bit 0: left control
				 * bit 1: left shift
				 * bit 2: left alt
				 * bit 3: left GUI, win
				 * bit 4: right contrl
				 * bit 5: right shift
				 * bit 6: right alt
				 * bit 7： right GUI, win? */
				switch (key_name[key_count]) {
					case 60:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 1;
						break;
					case 71: /* right shift */
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 5;
						break;
					case 75:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 0;
						break;
					case 76:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 3;
						break;
					case 77:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 2;
						break;
					case 83:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 6;
						break;
					case 85:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 4;
						break;
					default:
						break;

				}

			}
			else
			{
				s_UsbDeviceHidKeyboard.buffer[2 + key_count] = keyboard_map[key_name[key_count]];
			}
			key_name[key_count] = 0xff;
		}
    }

    //tick_end = SysTick->VAL;
    //tick_end = tick_start - tick_end;
    GPIO_PortClear(BOARD_USB_DEBUGIO_GPIO, 1U << BOARD_USB_DEBUGIO_GPIO_PIN);
    return USB_DeviceHidSend(s_UsbDeviceComposite->hidKeyboardHandle, USB_HID_KEYBOARD_ENDPOINT_IN,
                             s_UsbDeviceHidKeyboard.buffer, USB_HID_KEYBOARD_REPORT_LENGTH);
}

static usb_status_t USB_DeviceHidKeyboardAction_SmartDMA(void)
{
    static int x = 0U;
    static uint32_t count = 0;
    uint8_t key_valid = 0;
    uint32_t button_lev = 1;
    uint32_t tempa = 0;
    uint32_t tempb = 0;
    uint8_t num = 0xff;
    static uint32_t tick_start = 0;
    uint32_t tick_end = 0;
    static uint8_t pre_key_num = 0xff;
    count ++;
    static uint32_t test_key_count = 0;
    GPIO_PortSet(BOARD_USB_DEBUGIO_GPIO, 1U << BOARD_USB_DEBUGIO_GPIO_PIN);

    s_UsbDeviceHidKeyboard.buffer[2] = 0x00U;
    s_UsbDeviceHidKeyboard.buffer[0] = 0x00;
    memset(s_UsbDeviceHidKeyboard.buffer,0,6);


    /* software delay ++ */
    for(int i = 0; i < key_count_temp; i++)
    {
        key_delay[key_name_temp[i]]++;
    }

    for(int i = 0; i < 3; i ++)
    {
        for(int j = 0; j < 30; j++)
        {
        	button_lev = ((g_KeyValue[i] >> j) & 0x1);

            num = i * 30 + j;

            /* key state change, pressed or released */
        	if ((button_lev) != key_state_temp[num])
        	{
        		/* Pressed */
                if(button_lev)
                {
        			tick_start = SysTick->VAL;
        			key_state_temp[num] = 1;
        			key_name_temp[key_count_temp] = num;
					key_count_temp ++;
                }
                else   /* Released */
                {
                	key_state_temp[num] = 0;
                	for(int k = 0; k < key_count_temp; k++)
                	{
                		if(num == key_name_temp[i])
                		{
                			key_name_temp[i] = 0xFF;
                			break;
                		}
                	}
                	key_count_temp --;
                	key_delay[num] = 0;

                }
        	}
        	else /* state not change */
        	{

        	}
        }
    }

    for(int i = 0; i < key_count_temp; i++)
    {
        if(key_state_temp[key_name_temp[i]] == 1 && key_delay[key_name_temp[i]] == 0)
        {
        	tick_end = SysTick->VAL;
        	key_state[key_name_temp[i]] = 1;
            key_name[key_count] = key_name_temp[i];
            key_count ++;
            test_key_count ++;

        }
        /* detect a long pressed event, > 200ms */
        else if (  key_state_temp[key_name_temp[i]] == 1 && key_delay[key_name_temp[i]] >= 200 * KEYBOARD_INTERVAL_MS)
        {
        	key_state[key_name_temp[i]] = 1;
        	key_name[key_count] = key_name_temp[i];
        	key_count ++;
        }

    }
    if(key_count == 0)
    {
    	s_UsbDeviceHidKeyboard.buffer[2] = 0;
    }
    else
    {
    	if(key_count > 2)
    	{
    		test_key_count ++;
    	}
		while(key_count)
		{
			key_count --;
			if(key_name[key_count] == 60 || key_name[key_count] == 71 || key_name[key_count] == 75 || key_name[key_count] == 76 || key_name[key_count] == 77 || key_name[key_count] == 83 || key_name[key_count] == 84 || key_name[key_count] == 85)
			{
				/* bit 0: left control
				 * bit 1: left shift
				 * bit 2: left alt
				 * bit 3: left GUI, win
				 * bit 4: right contrl
				 * bit 5: right shift
				 * bit 6: right alt
				 * bit 7： right GUI, win? */
				switch (key_name[key_count]) {
					case 60:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 1;
						break;
					case 71: /* right shift */
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 5;
						break;
					case 75:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 0;
						break;
					case 76:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 3;
						break;
					case 77:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 2;
						break;
					case 83:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 6;
						break;
					case 85:
						s_UsbDeviceHidKeyboard.buffer[0] |= 1 << 4;
						break;
					default:
						break;

				}

			}
			else
			{
				s_UsbDeviceHidKeyboard.buffer[2 + key_count] = keyboard_map[key_name[key_count]];
			}
			key_name[key_count] = 0xff;
		}
    }

    //tick_end = SysTick->VAL;
    tick_end = tick_start - tick_end;
    GPIO_PortClear(BOARD_USB_DEBUGIO_GPIO, 1U << BOARD_USB_DEBUGIO_GPIO_PIN);
    return USB_DeviceHidSend(s_UsbDeviceComposite->hidKeyboardHandle, USB_HID_KEYBOARD_ENDPOINT_IN,
                             s_UsbDeviceHidKeyboard.buffer, USB_HID_KEYBOARD_REPORT_LENGTH);
}

usb_status_t USB_DeviceHidKeyboardCallback(class_handle_t handle, uint32_t event, void *param)
{
    usb_status_t error = kStatus_USB_InvalidRequest;

    switch (event)
    {
        case kUSB_DeviceHidEventSendResponse:
            if (s_UsbDeviceComposite->attach)
            {
#if USE_SMARTDMA_KEYSCAN == 1
            	return USB_DeviceHidKeyboardAction_SmartDMA();
#else
                return USB_DeviceHidKeyboardAction();
#endif
            }
            break;
        case kUSB_DeviceHidEventGetReport:
        case kUSB_DeviceHidEventSetReport:
        case kUSB_DeviceHidEventRequestReportBuffer:
            break;
        case kUSB_DeviceHidEventGetIdle:
        case kUSB_DeviceHidEventGetProtocol:
        case kUSB_DeviceHidEventSetIdle:
        case kUSB_DeviceHidEventSetProtocol:
            error = kStatus_USB_Success;
            break;
        default:
            break;
    }

    return error;
}

usb_status_t USB_DeviceHidKeyboardSetConfigure(class_handle_t handle, uint8_t configure)
{
    if (USB_COMPOSITE_CONFIGURE_INDEX == configure)
    {
        return USB_DeviceHidKeyboardAction(); /* run the cursor movement code */
    }
    return kStatus_USB_Error;
}

usb_status_t USB_DeviceHidKeyboardSetInterface(class_handle_t handle, uint8_t interface, uint8_t alternateSetting)
{
    if (USB_HID_KEYBOARD_INTERFACE_INDEX == interface)
    {
        return USB_DeviceHidKeyboardAction(); /* run the cursor movement code */
    }
    return kStatus_USB_Error;
}

usb_status_t USB_DeviceHidKeyboardInit(usb_device_composite_struct_t *deviceComposite)
{
    s_UsbDeviceComposite          = deviceComposite;
    s_UsbDeviceHidKeyboard.buffer = s_KeyboardBuffer;
    return kStatus_USB_Success;
}
