// SPDX-License-Identifier: MIT
// Copyright (C) 2026 Artem Senichev <artemsen@gmail.com>

#include "input.hpp"

#include <gtest/gtest.h>

#include <clocale>

// Mouse buttons, from <linux/input-event-codes.h>
#ifndef BTN_LEFT
#define BTN_LEFT   0x110
#define BTN_RIGHT  0x111
#define BTN_MIDDLE 0x112
#define BTN_SIDE   0x113
#define BTN_EXTRA  0x114
#endif

// NOLINTBEGIN(bugprone-unchecked-optional-access)

TEST(InputKeyboardTest, Load)
{
    std::setlocale(LC_ALL, "");

    std::optional<InputKeyboard> input;

    input = InputKeyboard::load("A");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_NONE);
    EXPECT_EQ(input->key, static_cast<xkb_keysym_t>(XKB_KEY_a));

    input = InputKeyboard::load("Ы");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_NONE);
    EXPECT_EQ(input->key, static_cast<xkb_keysym_t>(XKB_KEY_Cyrillic_YERU));

    input = InputKeyboard::load("Alt-Alt+1");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_ALT);
    EXPECT_EQ(input->key, static_cast<xkb_keysym_t>(XKB_KEY_1));

    input = InputKeyboard::load("Ctrl+Alt-Shift-Escape");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_CTRL | KEYMOD_ALT | KEYMOD_SHIFT);
    EXPECT_EQ(input->key, static_cast<xkb_keysym_t>(XKB_KEY_Escape));

    EXPECT_FALSE(InputKeyboard::load("123"));
    EXPECT_FALSE(InputKeyboard::load("AA"));
    EXPECT_FALSE(InputKeyboard::load("Ctrl"));
    EXPECT_FALSE(InputKeyboard::load("Ctrl+Alt"));
    EXPECT_FALSE(InputKeyboard::load("Ctrla+1"));
}

TEST(InputKeyboardTest, ToString)
{
    EXPECT_EQ(InputKeyboard('a', KEYMOD_NONE).to_string(), "a");
    EXPECT_EQ(InputKeyboard('a', KEYMOD_CTRL).to_string(), "Ctrl+a");
    EXPECT_EQ(InputKeyboard('A', KEYMOD_ALT).to_string(), "Alt+a");
    EXPECT_EQ(InputKeyboard('A', KEYMOD_SHIFT).to_string(), "Shift+a");

    EXPECT_EQ(
        InputKeyboard(XKB_KEY_Escape, KEYMOD_CTRL | KEYMOD_ALT | KEYMOD_SHIFT)
            .to_string(),
        "Ctrl+Alt+Shift+Escape");
}

TEST(InputMouseTest, Load)
{
    std::optional<InputMouse> input;

    input = InputMouse::load("MouseLeft");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_NONE);
    EXPECT_EQ(input->buttons, InputMouse::BUTTON_LEFT);

    input = InputMouse::load("MouseRight+ScrollUp");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_NONE);
    EXPECT_EQ(input->buttons, InputMouse::BUTTON_RIGHT | InputMouse::SCROLL_UP);

    input = InputMouse::load("Alt-MouseLeft");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_ALT);
    EXPECT_EQ(input->buttons, InputMouse::BUTTON_LEFT);

    input = InputMouse::load("Ctrl+Alt-Shift-MouseRight+ScrollUp");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->mods, KEYMOD_CTRL | KEYMOD_ALT | KEYMOD_SHIFT);
    EXPECT_EQ(input->buttons, InputMouse::BUTTON_RIGHT | InputMouse::SCROLL_UP);

    EXPECT_FALSE(InputMouse::load("AA"));
    EXPECT_FALSE(InputMouse::load("Ctrl"));
    EXPECT_FALSE(InputMouse::load("Ctrl+Alt"));
    EXPECT_FALSE(InputMouse::load("Ctrla+1"));
}

TEST(InputMouseTest, ToString)
{
    EXPECT_EQ(InputMouse(InputMouse::BUTTON_RIGHT, KEYMOD_NONE).to_string(),
              "MouseRight");
    EXPECT_EQ(InputMouse(InputMouse::BUTTON_LEFT, KEYMOD_CTRL).to_string(),
              "Ctrl+MouseLeft");
    EXPECT_EQ(InputMouse(InputMouse::BUTTON_LEFT,
                         KEYMOD_CTRL | KEYMOD_ALT | KEYMOD_SHIFT)
                  .to_string(),
              "Ctrl+Alt+Shift+MouseLeft");
}

TEST(InputMouseTest, Equality)
{
    const InputMouse a(InputMouse::BUTTON_LEFT, KEYMOD_CTRL);
    const InputMouse b(InputMouse::BUTTON_LEFT, KEYMOD_CTRL);
    const InputMouse c(InputMouse::BUTTON_RIGHT, KEYMOD_CTRL);
    const InputMouse d(InputMouse::BUTTON_LEFT, KEYMOD_ALT);

    EXPECT_TRUE(a == b);
    EXPECT_FALSE(a == c);
    EXPECT_FALSE(a == d);
}

TEST(InputMouseTest, Ordering)
{
    const InputMouse a(InputMouse::BUTTON_LEFT, KEYMOD_NONE);
    const InputMouse b(InputMouse::BUTTON_RIGHT, KEYMOD_NONE);
    const InputMouse c(InputMouse::BUTTON_LEFT, KEYMOD_CTRL);

    EXPECT_TRUE(a < b);
    EXPECT_TRUE(a < c);
    EXPECT_FALSE(b < a);
}

TEST(InputMouseTest, ToButton)
{
    EXPECT_EQ(InputMouse::to_button(BTN_LEFT), InputMouse::BUTTON_LEFT);
    EXPECT_EQ(InputMouse::to_button(BTN_RIGHT), InputMouse::BUTTON_RIGHT);
    EXPECT_EQ(InputMouse::to_button(BTN_MIDDLE), InputMouse::BUTTON_MIDDLE);
    EXPECT_EQ(InputMouse::to_button(BTN_SIDE), InputMouse::BUTTON_SIDE);
    EXPECT_EQ(InputMouse::to_button(BTN_EXTRA), InputMouse::BUTTON_EXTRA);
    EXPECT_EQ(InputMouse::to_button(999), InputMouse::NONE);
}

TEST(InputSignalTest, Load)
{
    std::optional<InputSignal> input;

    input = InputSignal::load("USR1");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->signal, InputSignal::USR1);

    input = InputSignal::load("USR2");
    ASSERT_TRUE(input);
    EXPECT_EQ(input->signal, InputSignal::USR2);

    EXPECT_FALSE(InputSignal::load("USR123"));
}

TEST(InputSignalTest, ToString)
{
    EXPECT_EQ(InputSignal(InputSignal::USR1).to_string(), "USR1");
    EXPECT_EQ(InputSignal(InputSignal::USR2).to_string(), "USR2");
}

TEST(InputSignalTest, Ordering)
{
    EXPECT_TRUE(InputSignal(InputSignal::USR1) <
                InputSignal(InputSignal::USR2));
    EXPECT_FALSE(InputSignal(InputSignal::USR2) <
                 InputSignal(InputSignal::USR1));
    EXPECT_FALSE(InputSignal(InputSignal::USR1) <
                 InputSignal(InputSignal::USR1));
}

// NOLINTEND(bugprone-unchecked-optional-access)
