#include <Core/Common/EnumUtils.h>
#include <Core/HAL/Event/Keyboard_Private.h>
#include <Core/HAL/Event/KeyboardMapping.h>
#include <Core/HAL/Event/SystemEvent.h>

namespace Juliet
{
    constexpr KeyboardID kGlobalKeyboardID = 0;

    namespace
    {
        struct KeyboardState
        {
            KeyState KeyState[ToUnderlying(ScanCode::Count)];
            KeyMod   KeyModState;
        } KeyboardState;

        bool SendKeyboardKey_Internal(uint64 timestamp, KeyboardID /*ID*/, Key key, KeyState keyState)
        {
            Assert(key.KeyCode == KeyCode::Unknown); // At this point Keycode is not yet extracted

            auto& keyboardState = KeyboardState; // Needed because MSVC debugger ignores variable in anonymouse namespace

            auto       type      = EventType::None;
            const bool isKeyDown = keyState == KeyState::Down;
            if (isKeyDown)
            {
                type = EventType::Key_Down;
            }
            else
            {
                type = EventType::Key_Up;
            }

            bool isKeyRepeat = false;
            if (key.ScanCode > ScanCode::Unknown && key.ScanCode < ScanCode::Count)
            {
                auto& currentKeyState = keyboardState.KeyState[ToUnderlying(key.ScanCode)];

                // If state didn't change, this is a key repeat
                if (isKeyDown)
                {
                    if (currentKeyState == KeyState::Down)
                    {
                        isKeyRepeat = true;
                    }
                }
                else
                {
                    if (currentKeyState == KeyState::Up)
                    {
                        return false;
                    }
                }

                keyboardState.KeyState[ToUnderlying(key.ScanCode)] = keyState;
                key.KeyCode = GetKeyCodeFromScanCode(key.ScanCode, keyboardState.KeyModState);
            }
            else if (key.Raw == 0)
            {
                return false;
            }

            if (!isKeyRepeat)
            {
                KeyMod newModifier = {};

                switch (key.KeyCode)
                {
                case KeyCode::LeftControl: newModifier = KeyMod::LeftControl; break;
                case KeyCode::RightControl: newModifier = KeyMod::RightControl; break;
                case KeyCode::LeftShift: newModifier = KeyMod::LeftShift; break;
                case KeyCode::RightShift: newModifier = KeyMod::RightShift; break;
                case KeyCode::LeftAlt: newModifier = KeyMod::LeftAlt; break;
                case KeyCode::RightAlt: newModifier = KeyMod::RightAlt; break;
                case KeyCode::LeftOSCommand: newModifier = KeyMod::LeftOSCommand; break;
                case KeyCode::RightOSCommand: newModifier = KeyMod::RightOSCommand; break;
                default: newModifier = KeyMod::None; break;
                }
                if (type == EventType::Key_Down)
                {
                    switch (key.KeyCode)
                    {
                    case KeyCode::NumlockClear: newModifier ^= KeyMod::NumLock; break;
                    case KeyCode::CapsLock: newModifier ^= KeyMod::CapsLock; break;
                    case KeyCode::ScrollLock: newModifier ^= KeyMod::ScrollLock; break;
                    default: keyboardState.KeyModState |= newModifier;
                    }
                }
                else
                {
                    // Remove from any keymod not pressed from the modifier
                    keyboardState.KeyModState &= ~newModifier;
                }
            }

            SystemEvent evt;
            evt.Timestamp                          = timestamp;
            evt.Type                               = type;
            evt.Data.Keyboard.AssociatedKeyboardID = kGlobalKeyboardID;
            evt.Data.Keyboard.Key                  = key;
            evt.Data.Keyboard.KeyState             = keyState;
            evt.Data.Keyboard.KeyModeState         = keyboardState.KeyModState;

            bool evtPosted = AddEvent(evt);

            return evtPosted;
        }
    } // namespace

    bool SendKeyboardKey(uint64 timestamp, KeyboardID ID, Key key, KeyState keyState)
    {
        return SendKeyboardKey_Internal(timestamp, ID, key, keyState);
    }

    bool IsKeyDown(ScanCode scanCode)
    {
        return KeyboardState.KeyState[ToUnderlying(scanCode)] == KeyState::Down;
    }

    KeyMod GetKeyModState()
    {
        auto& keyboardState = KeyboardState;
        return keyboardState.KeyModState;
    }

    KeyCode GetKeyCodeFromScanCode(ScanCode scanCode, KeyMod keyModState)
    {
        return GetKeyCodeFromDefaultMapping(scanCode, keyModState);
    }

    static_assert(sizeof(KeyState) == sizeof(bool));
    static_assert(ToUnderlying(KeyState::Down) == true);
    static_assert(ToUnderlying(KeyState::Up) == false);
    static_assert(sizeof(ScanCode) == sizeof(uint16));
    static_assert(ToUnderlying(ScanCode::Count) == 512);
} // namespace Juliet
