#include "keyboard.h"
#include <libc/src/unistd.h>
// 功能键标志变量
static bool shift_l = 0, shift_r = 0, ctrl_l = 0, ctrl_r = 0, alt_l = 0, alt_r = 0;
static bool gui_l = 0, gui_r = 0, apps = 0, insert = 0, home = 0, pgup = 0, del = 0, end = 0, pgdn = 0, arrow_u = 0, arrow_l = 0, arrow_d = 0, arrow_r = 0;
static bool kp_forward_slash = 0, kp_en = 0;

// 键盘扫描码有三种：
// 0xE1开头的PauseBreak键
// 0xE0开头的功能键
// 1byte的普通按键

// pause break键的扫描码，没错，它就是这么长
unsigned char pause_break_scan_code[] = {0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5};

// 第一套键盘扫描码 及其对应的字符
uint32_t keycode_map_normal[NUM_SCAN_CODES * MAP_COLS] =
    {
        /*scan-code	unShift		Shift		*/
        /*--------------------------------------------------------------*/
        /*0x00*/ 0,
        0,
        /*0x01*/ 0,
        0, // ESC
        /*0x02*/ '1',
        '!',
        /*0x03*/ '2',
        '@',
        /*0x04*/ '3',
        '#',
        /*0x05*/ '4',
        '$',
        /*0x06*/ '5',
        '%',
        /*0x07*/ '6',
        '^',
        /*0x08*/ '7',
        '&',
        /*0x09*/ '8',
        '*',
        /*0x0a*/ '9',
        '(',
        /*0x0b*/ '0',
        ')',
        /*0x0c*/ '-',
        '_',
        /*0x0d*/ '=',
        '+',
        /*0x0e*/ '\b',
        '\b', // BACKSPACE
        /*0x0f*/ '\t',
        '\t', // TAB

        /*0x10*/ 'q',
        'Q',
        /*0x11*/ 'w',
        'W',
        /*0x12*/ 'e',
        'E',
        /*0x13*/ 'r',
        'R',
        /*0x14*/ 't',
        'T',
        /*0x15*/ 'y',
        'Y',
        /*0x16*/ 'u',
        'U',
        /*0x17*/ 'i',
        'I',
        /*0x18*/ 'o',
        'O',
        /*0x19*/ 'p',
        'P',
        /*0x1a*/ '[',
        '{',
        /*0x1b*/ ']',
        '}',
        /*0x1c*/ '\n',
        '\n', // ENTER
        /*0x1d*/ 0x1d,
        0x1d, // CTRL Left
        /*0x1e*/ 'a',
        'A',
        /*0x1f*/ 's',
        'S',

        /*0x20*/ 'd',
        'D',
        /*0x21*/ 'f',
        'F',
        /*0x22*/ 'g',
        'G',
        /*0x23*/ 'h',
        'H',
        /*0x24*/ 'j',
        'J',
        /*0x25*/ 'k',
        'K',
        /*0x26*/ 'l',
        'L',
        /*0x27*/ ';',
        ':',
        /*0x28*/ '\'',
        '"',
        /*0x29*/ '`',
        '~',
        /*0x2a*/ 0x2a,
        0x2a, // SHIFT Left
        /*0x2b*/ '\\',
        '|',
        /*0x2c*/ 'z',
        'Z',
        /*0x2d*/ 'x',
        'X',
        /*0x2e*/ 'c',
        'C',
        /*0x2f*/ 'v',
        'V',

        /*0x30*/ 'b',
        'B',
        /*0x31*/ 'n',
        'N',
        /*0x32*/ 'm',
        'M',
        /*0x33*/ ',',
        '<',
        /*0x34*/ '.',
        '>',
        /*0x35*/ '/',
        '?',
        /*0x36*/ 0x36,
        0x36, // SHIFT Right
        /*0x37*/ '*',
        '*',
        /*0x38*/ 0x38,
        0x38, // ALT Left
        /*0x39*/ ' ',
        ' ',
        /*0x3a*/ 0,
        0, // CAPS LOCK
        /*0x3b*/ 0,
        0, // F1
        /*0x3c*/ 0,
        0, // F2
        /*0x3d*/ 0,
        0, // F3
        /*0x3e*/ 0,
        0, // F4
        /*0x3f*/ 0,
        0, // F5

        /*0x40*/ 0,
        0, // F6
        /*0x41*/ 0,
        0, // F7
        /*0x42*/ 0,
        0, // F8
        /*0x43*/ 0,
        0, // F9
        /*0x44*/ 0,
        0, // F10
        /*0x45*/ 0,
        0, // NUM LOCK
        /*0x46*/ 0,
        0, // SCROLL LOCK
        /*0x47*/ '7',
        0, /*PAD HONE*/
        /*0x48*/ '8',
        0, /*PAD UP*/
        /*0x49*/ '9',
        0, /*PAD PAGEUP*/
        /*0x4a*/ '-',
        0, /*PAD MINUS*/
        /*0x4b*/ '4',
        0, /*PAD LEFT*/
        /*0x4c*/ '5',
        0, /*PAD MID*/
        /*0x4d*/ '6',
        0, /*PAD RIGHT*/
        /*0x4e*/ '+',
        0, /*PAD PLUS*/
        /*0x4f*/ '1',
        0, /*PAD END*/

        /*0x50*/ '2',
        0, /*PAD DOWN*/
        /*0x51*/ '3',
        0, /*PAD PAGEDOWN*/
        /*0x52*/ '0',
        0, /*PAD INS*/
        /*0x53*/ '.',
        0, /*PAD DOT*/
        /*0x54*/ 0,
        0,
        /*0x55*/ 0,
        0,
        /*0x56*/ 0,
        0,
        /*0x57*/ 0,
        0, // F11
        /*0x58*/ 0,
        0, // F12
        /*0x59*/ 0,
        0,
        /*0x5a*/ 0,
        0,
        /*0x5b*/ 0,
        0,
        /*0x5c*/ 0,
        0,
        /*0x5d*/ 0,
        0,
        /*0x5e*/ 0,
        0,
        /*0x5f*/ 0,
        0,

        /*0x60*/ 0,
        0,
        /*0x61*/ 0,
        0,
        /*0x62*/ 0,
        0,
        /*0x63*/ 0,
        0,
        /*0x64*/ 0,
        0,
        /*0x65*/ 0,
        0,
        /*0x66*/ 0,
        0,
        /*0x67*/ 0,
        0,
        /*0x68*/ 0,
        0,
        /*0x69*/ 0,
        0,
        /*0x6a*/ 0,
        0,
        /*0x6b*/ 0,
        0,
        /*0x6c*/ 0,
        0,
        /*0x6d*/ 0,
        0,
        /*0x6e*/ 0,
        0,
        /*0x6f*/ 0,
        0,

        /*0x70*/ 0,
        0,
        /*0x71*/ 0,
        0,
        /*0x72*/ 0,
        0,
        /*0x73*/ 0,
        0,
        /*0x74*/ 0,
        0,
        /*0x75*/ 0,
        0,
        /*0x76*/ 0,
        0,
        /*0x77*/ 0,
        0,
        /*0x78*/ 0,
        0,
        /*0x79*/ 0,
        0,
        /*0x7a*/ 0,
        0,
        /*0x7b*/ 0,
        0,
        /*0x7c*/ 0,
        0,
        /*0x7d*/ 0,
        0,
        /*0x7e*/ 0,
        0,
        /*0x7f*/ 0,
        0,
};

/**
 * @brief 解析键盘扫描码
 *
 */
int keyboard_analyze_keycode(int fd)
{
    bool flag_make = false;

    int c = keyboard_get_scancode(fd);
    // 循环队列为空
    if (c == -1)
        return 0;

    unsigned char scancode = (unsigned char)c;

    int key = 0;
    if (scancode == 0xE1) // Pause Break
    {
        key = PAUSE_BREAK;
        // 清除缓冲区中剩下的扫描码
        for (int i = 1; i < 6; ++i)
            if (keyboard_get_scancode(fd) != pause_break_scan_code[i])
            {
                key = 0;
                break;
            }
    }
    else if (scancode == 0xE0) // 功能键, 有多个扫描码
    {
        // 获取下一个扫描码
        scancode = keyboard_get_scancode(fd);
        switch (scancode)
        {
        case 0x2a: // print screen 按键被按下
            if (keyboard_get_scancode(fd) == 0xe0)
                if (keyboard_get_scancode(fd) == 0x37)
                {
                    key = PRINT_SCREEN;
                    flag_make = true;
                }
            break;
        case 0xb7: // print screen 按键被松开
            if (keyboard_get_scancode(fd) == 0xe0)
                if (keyboard_get_scancode(fd) == 0xaa)
                {
                    key = PRINT_SCREEN;
                    flag_make = false;
                }
            break;
        case 0x1d: // 按下右边的ctrl
            ctrl_r = true;
            key = OTHER_KEY;
            break;
        case 0x9d: // 松开右边的ctrl
            ctrl_r = false;
            key = OTHER_KEY;
            break;
        case 0x38: // 按下右边的alt
            alt_r = true;
            key = OTHER_KEY;
            break;
        case 0xb8: // 松开右边的alt
            alt_r = false;
            key = OTHER_KEY;
            break;
        case 0x5b:
            gui_l = true;
            key = OTHER_KEY;
            break;
        case 0xdb:
            gui_l = false;
            key = OTHER_KEY;
            break;
        case 0x5c:
            gui_r = true;
            key = OTHER_KEY;
            break;
        case 0xdc:
            gui_r = false;
            key = OTHER_KEY;
            break;
        case 0x5d:
            apps = true;
            key = OTHER_KEY;
            break;
        case 0xdd:
            apps = false;
            key = OTHER_KEY;
            break;
        case 0x52:
            insert = true;
            key = OTHER_KEY;
            break;
        case 0xd2:
            insert = false;
            key = OTHER_KEY;
            break;
        case 0x47:
            home = true;
            key = OTHER_KEY;
            break;
        case 0xc7:
            home = false;
            key = OTHER_KEY;
            break;
        case 0x49:
            pgup = true;
            key = OTHER_KEY;
            break;
        case 0xc9:
            pgup = false;
            key = OTHER_KEY;
            break;
        case 0x53:
            del = true;
            key = OTHER_KEY;
            break;
        case 0xd3:
            del = false;
            key = OTHER_KEY;
            break;
        case 0x4f:
            end = true;
            key = OTHER_KEY;
            break;
        case 0xcf:
            end = false;
            key = OTHER_KEY;
            break;
        case 0x51:
            pgdn = true;
            key = OTHER_KEY;
            break;
        case 0xd1:
            pgdn = false;
            key = OTHER_KEY;
            break;
        case 0x48:
            arrow_u = true;
            key = OTHER_KEY;
            break;
        case 0xc8:
            arrow_u = false;
            key = OTHER_KEY;
            return 0xc8;
            break;
        case 0x4b:
            arrow_l = true;
            key = OTHER_KEY;
            break;
        case 0xcb:
            arrow_l = false;
            key = OTHER_KEY;
            break;
        case 0x50:
            arrow_d = true;
            key = OTHER_KEY;
            return 0x50;
            break;
        case 0xd0:
            arrow_d = false;
            key = OTHER_KEY;
            break;
        case 0x4d:
            arrow_r = true;
            key = OTHER_KEY;
            break;
        case 0xcd:
            arrow_r = false;
            key = OTHER_KEY;
            break;

        case 0x35: // 数字小键盘的 / 符号
            kp_forward_slash = true;
            key = OTHER_KEY;
            break;
        case 0xb5:
            kp_forward_slash = false;
            key = OTHER_KEY;
            break;
        case 0x1c:
            kp_en = true;
            key = OTHER_KEY;
            break;
        case 0x9c:
            kp_en = false;
            key = OTHER_KEY;
            break;

        default:
            key = OTHER_KEY;
            break;
        }
    }

    if (key == 0) // 属于第三类扫描码
    {
        // 判断按键是被按下还是抬起
        flag_make = ((scancode & FLAG_BREAK) ? 0 : 1);

        // 计算扫描码位于码表的第几行
        uint32_t *key_row = &keycode_map_normal[(scancode & 0x7f) * MAP_COLS];
        unsigned char col = 0;
        // shift被按下
        if (shift_l || shift_r)
            col = 1;
        key = key_row[col];

        switch (scancode & 0x7f)
        {
        case 0x2a:
            shift_l = flag_make;
            key = 0;
            break;
        case 0x36:
            shift_r = flag_make;
            key = 0;
            break;
        case 0x1d:
            ctrl_l = flag_make;
            key = 0;
            break;
        case 0x38:
            ctrl_r = flag_make;
            key = 0;
            break;
        default:
            if (!flag_make)
                key = 0;
            break;
        }
        if (key)
            return key;
    }
    return 0;
}

/**
 * @brief 从键盘设备文件中获取键盘扫描码
 *
 */
int keyboard_get_scancode(int fd)
{
    unsigned int ret = 0;
    read(fd, &ret, 1);
    return ret;
}