/**
 * Copyright (C) 2022 Matt Burt, all xrdp contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/**
 * @file    common/scancode.c
 * @brief   Scancode handling
 */

#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stddef.h>
#include <string.h>

#include "scancode.h"

struct scancode_to_keycode
{
    unsigned short scancode; // 0x1xx implies an extended key
    unsigned char keycode;
};

#define ELEMENTS(x) (sizeof(x) / sizeof(x[0]))

// Sources:-
// file:/usr/share/X11/xkb/keycodes/evdev
// https://wiki.osdev.org/PS/2_Keyboard
// https://www.kbdlayout.info/
static const struct scancode_to_keycode
    evdev_scancode_to_keycode_map[] =
{
    //                   Virtual              XKB
    //                   Key (US)             Symbol
    //                   --------             ------
    { 0x001, 9 },   //   VK_ESCAPE            ESC
    { 0x002, 10 },  //   VK_1                 AE01
    { 0x003, 11 },  //   VK_2                 AE02
    { 0x004, 12 },  //   VK_3                 AE03
    { 0x005, 13 },  //   VK_4                 AE04
    { 0x006, 14 },  //   VK_5                 AE05
    { 0x007, 15 },  //   VK_6                 AE06
    { 0x008, 16 },  //   VK_7                 AE07
    { 0x009, 17 },  //   VK_8                 AE08
    { 0x00a, 18 },  //   VK_9                 AE09
    { 0x00b, 19 },  //   VK_0                 AE10
    { 0x00c, 20 },  //   VK_OEM_MINUS         AE11
    { 0x00d, 21 },  //   VK_OEM_PLUS          AE12
    { 0x00e, 22 },  //   VK_BACK              BKSP
    { 0x00f, 23 },  //   VK_TAB               TAB
    { 0x010, 24 },  //   VK_Q                 AD01
    { 0x011, 25 },  //   VK_W                 AD02
    { 0x012, 26 },  //   VK_E                 AD03
    { 0x013, 27 },  //   VK_R                 AD04
    { 0x014, 28 },  //   VK_T                 AD05
    { 0x015, 29 },  //   VK_Y                 AD06
    { 0x016, 30 },  //   VK_U                 AD07
    { 0x017, 31 },  //   VK_I                 AD08
    { 0x018, 32 },  //   VK_O                 AD09
    { 0x019, 33 },  //   VK_P                 AD10
    { 0x01a, 34 },  //   VK_OEM_4             AD11
    { 0x01b, 35 },  //   VK_OEM_6             AD12
    { 0x01c, 36 },  //   VK_RETURN            RTRN
    { 0x01d, 37 },  //   VK_LCONTROL          LCTL
    { 0x01e, 38 },  //   VK_A                 AC01
    { 0x01f, 39 },  //   VK_S                 AC02
    { 0x020, 40 },  //   VK_D                 AC03
    { 0x021, 41 },  //   VK_F                 AC04
    { 0x022, 42 },  //   VK_G                 AC05
    { 0x023, 43 },  //   VK_H                 AC06
    { 0x024, 44 },  //   VK_J                 AC07
    { 0x025, 45 },  //   VK_K                 AC08
    { 0x026, 46 },  //   VK_L                 AC09
    { 0x027, 47 },  //   VK_OEM_1             AC10
    { 0x028, 48 },  //   VK_OEM_7             AC11
    { 0x029, 49 },  //   VK_OEM_3             TLDE
    { 0x02a, 50 },  //   VK_LSHIFT            LFSH
    { 0x02b, 51 },  //   VK_OEM_5             BKSL
    { 0x02c, 52 },  //   VK_Z                 AB01
    { 0x02d, 53 },  //   VK_X                 AB02
    { 0x02e, 54 },  //   VK_C                 AB03
    { 0x02f, 55 },  //   VK_V                 AB04
    { 0x030, 56 },  //   VK_B                 AB05
    { 0x031, 57 },  //   VK_N                 AB06
    { 0x032, 58 },  //   VK_M                 AB07
    { 0x033, 59 },  //   VK_OEM_COMMA         AB08
    { 0x034, 60 },  //   VK_OEM_PERIOD        AB09
    { 0x035, 61 },  //   VK_OEM_2             AB10
    { 0x036, 62 },  //   VK_RSHIFT            RTSH
    { 0x037, 63 },  //   VK_MULTIPLY          KPMU
    { 0x038, 64 },  //   VK_LMENU             LALT
    { 0x039, 65 },  //   VK_SPACE             SPCE
    { 0x03a, 66 },  //   VK_CAPITAL           CAPS
    { 0x03b, 67 },  //   VK_F1                FK01
    { 0x03c, 68 },  //   VK_F2                FK02
    { 0x03d, 69 },  //   VK_F3                FK03
    { 0x03e, 70 },  //   VK_F4                FK04
    { 0x03f, 71 },  //   VK_F5                FK05
    { 0x040, 72 },  //   VK_F6                FK06
    { 0x041, 73 },  //   VK_F7                FK07
    { 0x042, 74 },  //   VK_F8                FK08
    { 0x043, 75 },  //   VK_F9                FK09
    { 0x044, 76 },  //   VK_F10               FK10
    { 0x045, 77 },  //   VK_NUMLOCK           NMLK
    { 0x046, 78 },  //   VK_SCROLL            SCLK
    { 0x047, 79 },  //   VK_HOME              KP7
    { 0x048, 80 },  //   VK_UP                KP8
    { 0x049, 81 },  //   VK_PRIOR             KP9
    { 0x04a, 82 },  //   VK_SUBTRACT          KPSU
    { 0x04b, 83 },  //   VK_LEFT              KP4
    { 0x04c, 84 },  //   VK_CLEAR             KP5
    { 0x04d, 85 },  //   VK_RIGHT             KP6
    { 0x04e, 86 },  //   VK_ADD               KPAD
    { 0x04f, 87 },  //   VK_END               KP1
    { 0x050, 88 },  //   VK_DOWN              KP2
    { 0x051, 89 },  //   VK_NEXT              KP3
    { 0x052, 90 },  //   VK_INSERT            KP0
    { 0x053, 91 },  //   VK_DELETE            KPDL
    { 0x056, 94 },  //   VK_OEM_102           LSGT
    { 0x057, 95 },  //   VK_F11               FK11
    { 0x058, 96 },  //   VK_F12               FK12
    { 0x070, 101 }, //   -                    HKTG
    { 0x073, 97 },  //   VK_ABNT_C1           AB11
    { 0x079, 100 }, //   -                    HENK
    { 0x07b, 102 }, //   VK_OEM_PA1           MUHE
    { 0x07d, 132 }, //   -                    AE13
    { 0x07e, 129 }, //   VK_ABNT_C2           KPPT (Brazil ABNT2)
    { 0x110, 173 }, //   VK_MEDIA_PREV_TRACK  I173 (KEY_PREVIOUSSONG)
    { 0x119, 171 }, //   VK_MEDIA_NEXT_TRACK  I171 (KEY_NEXTSONG)
    { 0x11c, 104 }, //   VK_RETURN            KPEN
    { 0x11d, 105 }, //   VK_RCONTROL          RCTL
    { 0x120, 121 }, //   VK_VOLUME_MUTE       MUTE
    { 0x121, 148 }, //   VK_LAUNCH_APP2       I148 (KEY_CALC)
    { 0x122, 172 }, //   VK_PLAY_PAUSE        I172 (KEY_PLAYPAUSE)
    { 0x124, 174 }, //   VK_MEDIA_STOP        I174 (KEY_STOPCD)
    { 0x12e, 122 }, //   VK_VOLUME_DOWN       VOL-
    { 0x130, 123 }, //   VK_VOLUME_UP         VOL+
    { 0x132, 180 }, //   VK_BROWSER_HOME      I180 (KEY_HOMEPAGE)
    { 0x135, 106 }, //   VK_DIVIDE            KPDV
    { 0x137, 107 }, //   VK_SNAPSHOT          PRSC
    { 0x138, 108 }, //   VK_RMENU             RALT
    { 0x147, 110 }, //   VK_HOME              HOME
    { 0x148, 111 }, //   VK_UP                UP
    { 0x149, 112 }, //   VK_PRIOR             PGUP (KEY_COMPUTER)
    { 0x14b, 113 }, //   VK_LEFT              LEFT
    { 0x14d, 114 }, //   VK_RIGHT             RGHT
    { 0x14f, 115 }, //   VK_END               END
    { 0x150, 116 }, //   VK_DOWN              DOWN
    { 0x151, 117 }, //   VK_NEXT              PGDN
    { 0x152, 118 }, //   VK_INSERT            INS
    { 0x153, 119 }, //   VK_DELETE            DELE
    { 0x15b, 133 }, //   VK_LWIN              LWIN
    { 0x15c, 134 }, //   VK_RWIN              RWIN
    { 0x15d, 135 }, //   VK_APPS              COMP
    { 0x165, 225 }, //   VK_BROWSER_SEARCH    I225 (KEY_SEARCH)
    { 0x166, 164 }, //   VK_BROWSER_FAVORITES I164 (KEY_BOOKMARKS)
    { 0x16b, 165 }, //   VK_LAUNCH_APP1       I165 (KEY_COMPUTER)
    { 0x16c, 163 }, //   VK_LAUNCH_MAIL       I163 (KEY_MAIL)
    { 0x21d, 127 }  //   VK_PAUSE             PAUS (KEY_PAUSE)
};

// Sources:-
// file:/usr/share/X11/xkb/keycodes/xfree86
// https://wiki.osdev.org/PS/2_Keyboard
// https://www.kbdlayout.info/
static const struct scancode_to_keycode
    base_scancode_to_keycode_map[] =
{
    //                   Virtual              XKB
    //                   Key (US)             Symbol
    //                   --------             ------
    { 0x001, 9 },   //   VK_ESCAPE            ESC
    { 0x002, 10 },  //   VK_1                 AE01
    { 0x003, 11 },  //   VK_2                 AE02
    { 0x004, 12 },  //   VK_3                 AE03
    { 0x005, 13 },  //   VK_4                 AE04
    { 0x006, 14 },  //   VK_5                 AE05
    { 0x007, 15 },  //   VK_6                 AE06
    { 0x008, 16 },  //   VK_7                 AE07
    { 0x009, 17 },  //   VK_8                 AE08
    { 0x00a, 18 },  //   VK_9                 AE09
    { 0x00b, 19 },  //   VK_0                 AE10
    { 0x00c, 20 },  //   VK_OEM_MINUS         AE11
    { 0x00d, 21 },  //   VK_OEM_PLUS          AE12
    { 0x00e, 22 },  //   VK_BACK              BKSP
    { 0x00f, 23 },  //   VK_TAB               TAB
    { 0x010, 24 },  //   VK_Q                 AD01
    { 0x011, 25 },  //   VK_W                 AD02
    { 0x012, 26 },  //   VK_E                 AD03
    { 0x013, 27 },  //   VK_R                 AD04
    { 0x014, 28 },  //   VK_T                 AD05
    { 0x015, 29 },  //   VK_Y                 AD06
    { 0x016, 30 },  //   VK_U                 AD07
    { 0x017, 31 },  //   VK_I                 AD08
    { 0x018, 32 },  //   VK_O                 AD09
    { 0x019, 33 },  //   VK_P                 AD10
    { 0x01a, 34 },  //   VK_OEM_4             AD11
    { 0x01b, 35 },  //   VK_OEM_6             AD12
    { 0x01c, 36 },  //   VK_RETURN            RTRN
    { 0x01d, 37 },  //   VK_LCONTROL          LCTL
    { 0x01e, 38 },  //   VK_A                 AC01
    { 0x01f, 39 },  //   VK_S                 AC02
    { 0x020, 40 },  //   VK_D                 AC03
    { 0x021, 41 },  //   VK_F                 AC04
    { 0x022, 42 },  //   VK_G                 AC05
    { 0x023, 43 },  //   VK_H                 AC06
    { 0x024, 44 },  //   VK_J                 AC07
    { 0x025, 45 },  //   VK_K                 AC08
    { 0x026, 46 },  //   VK_L                 AC09
    { 0x027, 47 },  //   VK_OEM_1             AC10
    { 0x028, 48 },  //   VK_OEM_7             AC11
    { 0x029, 49 },  //   VK_OEM_3             TLDE
    { 0x02a, 50 },  //   VK_LSHIFT            LFSH
    { 0x02b, 51 },  //   VK_OEM_5             BKSL
    { 0x02c, 52 },  //   VK_Z                 AB01
    { 0x02d, 53 },  //   VK_X                 AB02
    { 0x02e, 54 },  //   VK_C                 AB03
    { 0x02f, 55 },  //   VK_V                 AB04
    { 0x030, 56 },  //   VK_B                 AB05
    { 0x031, 57 },  //   VK_N                 AB06
    { 0x032, 58 },  //   VK_M                 AB07
    { 0x033, 59 },  //   VK_OEM_COMMA         AB08
    { 0x034, 60 },  //   VK_OEM_PERIOD        AB09
    { 0x035, 61 },  //   VK_OEM_2             AB10
    { 0x036, 62 },  //   VK_RSHIFT            RTSH
    { 0x037, 63 },  //   VK_MULTIPLY          KPMU
    { 0x038, 64 },  //   VK_LMENU             LALT
    { 0x039, 65 },  //   VK_SPACE             SPCE
    { 0x03a, 66 },  //   VK_CAPITAL           CAPS
    { 0x03b, 67 },  //   VK_F1                FK01
    { 0x03c, 68 },  //   VK_F2                FK02
    { 0x03d, 69 },  //   VK_F3                FK03
    { 0x03e, 70 },  //   VK_F4                FK04
    { 0x03f, 71 },  //   VK_F5                FK05
    { 0x040, 72 },  //   VK_F6                FK06
    { 0x041, 73 },  //   VK_F7                FK07
    { 0x042, 74 },  //   VK_F8                FK08
    { 0x043, 75 },  //   VK_F9                FK09
    { 0x044, 76 },  //   VK_F10               FK10
    { 0x045, 77 },  //   VK_NUMLOCK           NMLK
    { 0x046, 78 },  //   VK_SCROLL            SCLK
    { 0x047, 79 },  //   VK_HOME              KP7
    { 0x048, 80 },  //   VK_UP                KP8
    { 0x049, 81 },  //   VK_PRIOR             KP9
    { 0x04a, 82 },  //   VK_SUBTRACT          KPSU
    { 0x04b, 83 },  //   VK_LEFT              KP4
    { 0x04c, 84 },  //   VK_CLEAR             KP5
    { 0x04d, 85 },  //   VK_RIGHT             KP6
    { 0x04e, 86 },  //   VK_ADD               KPAD
    { 0x04f, 87 },  //   VK_END               KP1
    { 0x050, 88 },  //   VK_DOWN              KP2
    { 0x051, 89 },  //   VK_NEXT              KP3
    { 0x052, 90 },  //   VK_INSERT            KP0
    { 0x053, 91 },  //   VK_DELETE            KPDL
    { 0x056, 94 },  //   VK_OEM_102           LSGT
    { 0x057, 95 },  //   VK_F11               FK11
    { 0x058, 96 },  //   VK_F12               FK12
    { 0x070, 208 }, //   -                    HKTG
    { 0x073, 211 }, //   VK_ABNT_C1           AB11
    { 0x079, 129 }, //   -                    XFER
    { 0x07b, 131 }, //   VK_OEM_PA1           NFER
    { 0x07d, 133 }, //   -                    AE13
    { 0x07e, 134 }, //   VK_ABNT_C2           KPPT (Brazil ABNT2)
    { 0x11c, 108 }, //   VK_RETURN            KPEN
    { 0x11d, 109 }, //   VK_RCONTROL          RCTL
    { 0x120, 141 }, //   VK_VOLUME_MUTE       MUTE
    { 0x12e, 142 }, //   VK_VOLUME_DOWN       VOL-
    { 0x130, 143 }, //   VK_VOLUME_UP         VOL+
    { 0x135, 112 }, //   VK_DIVIDE            KPDV
    { 0x137, 121 }, //   VK_SNAPSHOT          PRSC
    { 0x138, 113 }, //   VK_RMENU             RALT
    { 0x147, 97 },  //   VK_HOME              HOME
    { 0x148, 98 },  //   VK_UP                UP
    { 0x149, 99 },  //   VK_PRIOR             PGUP (KEY_COMPUTER)
    { 0x14b, 100 }, //   VK_LEFT              LEFT
    { 0x14d, 102 }, //   VK_RIGHT             RGHT
    { 0x14f, 103 }, //   VK_END               END
    { 0x150, 104 }, //   VK_DOWN              DOWN
    { 0x151, 105 }, //   VK_NEXT              PGDN
    { 0x152, 106 }, //   VK_INSERT            INS
    { 0x153, 107 }, //   VK_DELETE            DELE
    { 0x15b, 115 }, //   VK_LWIN              LWIN
    { 0x15c, 116 }, //   VK_RWIN              RWIN
    { 0x15d, 117 }, //   VK_APPS              COMP
    { 0x21d, 110 }  //   VK_PAUSE             PAUS (KEY_PAUSE)
};

struct map_settings
{
    const struct scancode_to_keycode *map;
    const char *name;
    unsigned int size;
};

enum settings_index
{
    SI_EVDEV = 0,
    SI_BASE
};

const struct map_settings global_settings[] =
{
    {
        // SI_EVDEV
        .map = evdev_scancode_to_keycode_map,
        .name = "evdev",
        .size = ELEMENTS(evdev_scancode_to_keycode_map),
    },
    {
        // SI_BASE
        .map = base_scancode_to_keycode_map,
        .name = "base",
        .size = ELEMENTS(base_scancode_to_keycode_map)
    }
};

// Default mapping set is "evdev"
const struct map_settings *settings = &global_settings[SI_EVDEV];

/*****************************************************************************/
int
scancode_to_index(unsigned short scancode)
{
    if (scancode <= 0x7f)
    {
        return scancode;
    }
    if (scancode <= 0xff)
    {
        // 0x80 - 0xff : Invalid code
        return -1;
    }
    if (scancode <= 0x177)
    {
        // 01x100 - 0x177 : Move bit 9 to bit 8
        return (scancode & 0x7f) | 0x80;
    }

    if (scancode == SCANCODE_PAUSE_KEY)
    {
        return SCANCODE_INDEX_PAUSE_KEY;
    }

    // This leaves the following which are all rejected
    // 0x178 - 0x17f (currently unused). These would map to indexes 0xf8
    //               to 0xff which we are reserving for extended1 keys.
    // 0x180 - 0x1ff Illegal format
    // >0x200        Anything not mentioned explicitly above (e.g.
    //               SCANCODE_PAUSE_KEY)
    return -1;
}

/*****************************************************************************/
unsigned short
scancode_from_index(int index)
{
    unsigned short result = index & 0xff;

    if (result == SCANCODE_INDEX_PAUSE_KEY)
    {
        result = SCANCODE_PAUSE_KEY;
    }
    else if ((result & 0x80) != 0)
    {
        result ^= 0x180; // Clear bit 7, set bit 8
    }
    return result;
}

/*****************************************************************************/
unsigned short
scancode_to_x11_keycode(unsigned short scancode)
{
    unsigned int min = 0;
    unsigned int max = settings->size;
    unsigned short rv = 0;

    // Check scancode is in range of map
    if (scancode >= settings->map[min].scancode &&
            scancode <= settings->map[max - 1].scancode)
    {
        // Use a binary chop to locate the correct index
        // in logarithmic time.
        while (1)
        {
            unsigned int index = (min + max) / 2;
            if (scancode == settings->map[index].scancode)
            {
                rv = settings->map[index].keycode;
                break;
            }
            // Adjust min or max, checking for end
            // of iteration
            if (scancode < settings->map[index].scancode)
            {
                max = index;
            }
            else if (min == index)
            {
                // We've already checked this value
                break;
            }
            else
            {
                min = index;
            }
        }
    }

    return rv;
}

/*****************************************************************************/
unsigned short
scancode_get_next(unsigned int *iter)
{
    unsigned short rv = 0;
    if (*iter < settings->size)
    {
        rv = settings->map[*iter].scancode;
        ++(*iter);
    }

    return rv;
}

/*****************************************************************************/
int
scancode_set_keycode_set(const char *kk_set)
{
    int rv = 0;

    if (kk_set == NULL)
    {
        rv = 1;
    }
    else if (strncmp(kk_set, "evdev", 5) == 0)
    {
        settings = &global_settings[SI_EVDEV];
    }
    else if (strncmp(kk_set, "base", 4) == 0)
    {
        settings = &global_settings[SI_BASE];
    }
    else if (strncmp(kk_set, "xfree86", 7) == 0)
    {
        settings = &global_settings[SI_BASE];
    }
    else
    {
        rv = 1;
    }

    return rv;
}

/*****************************************************************************/
const char *
scancode_get_keycode_set(void)
{
    return settings->name;
}

/*****************************************************************************/
const char *
scancode_get_xkb_rules(void)
{
    // Currently supported keycods map directly to the same name for
    // the rules which use them.
    return settings->name;
}
