#include "input/non_blocking_actuator.hpp"
#include <chrono>

namespace input {

NonBlockingActuator::NonBlockingActuator(KMBoxNet& kmbox)
    : m_kmbox(kmbox) {
}

void NonBlockingActuator::ProcessPendingReleases(uint64_t nowMs) {
    if (!m_kmbox.IsGameWindowFocused()) {
        // Cửa sổ game không được focus -> Ngay lập tức giải phóng toàn bộ phím đang chờ và đồng bộ phần cứng
        ReleaseAll();
        return;
    }

    for (auto& slot : m_activeSlots) {
        if (slot.active && nowMs >= slot.releaseTimestampMs) {
            ReleaseSlot(slot);
            slot.active = false;
            slot.type = ActuatorActionType::None;
        }
    }
}

bool NonBlockingActuator::KeyPressVk(uint8_t vk, uint32_t dwellTimeMs, uint64_t nowMs) {
    if (!m_kmbox.IsGameWindowFocused()) {
        return false;
    }

    const auto start = std::chrono::high_resolution_clock::now();

    // 1. Gửi ngay lập tức KeyDown tới thiết bị ngoại vi (0ms sleep!)
    if (!m_kmbox.KeyDownVk(vk)) {
        return false;
    }

    // 2. Đăng ký hạn giải phóng phím theo dwell-time
    const bool ok = RegisterRelease(ActuatorActionType::KeyboardVk, vk, 0, 0, nowMs + dwellTimeMs);

    const auto end = std::chrono::high_resolution_clock::now();
    m_lastDispatchTimeUs = std::chrono::duration<double, std::micro>(end - start).count();

    return ok;
}

bool NonBlockingActuator::KeyPressHid(uint8_t hid, uint32_t dwellTimeMs, uint64_t nowMs) {
    if (!m_kmbox.IsGameWindowFocused()) {
        return false;
    }

    const auto start = std::chrono::high_resolution_clock::now();

    if (!m_kmbox.KeyDown(hid)) {
        return false;
    }

    const bool ok = RegisterRelease(ActuatorActionType::KeyboardHid, 0, hid, 0, nowMs + dwellTimeMs);

    const auto end = std::chrono::high_resolution_clock::now();
    m_lastDispatchTimeUs = std::chrono::duration<double, std::micro>(end - start).count();

    return ok;
}

bool NonBlockingActuator::ClickMouse(int button, uint32_t dwellTimeMs, uint64_t nowMs) {
    if (!m_kmbox.IsGameWindowFocused()) {
        return false;
    }

    const auto start = std::chrono::high_resolution_clock::now();

    if (!m_kmbox.MouseDown(button)) {
        return false;
    }

    const bool ok = RegisterRelease(ActuatorActionType::MouseButton, 0, 0, button, nowMs + dwellTimeMs);

    const auto end = std::chrono::high_resolution_clock::now();
    m_lastDispatchTimeUs = std::chrono::duration<double, std::micro>(end - start).count();

    return ok;
}

void NonBlockingActuator::ReleaseAll() {
    for (auto& slot : m_activeSlots) {
        if (slot.active) {
            ReleaseSlot(slot);
            slot.active = false;
            slot.type = ActuatorActionType::None;
        }
    }
    m_kmbox.ReleaseAllKeys();
}

size_t NonBlockingActuator::ActiveSlotCount() const {
    size_t count = 0;
    for (const auto& slot : m_activeSlots) {
        if (slot.active) ++count;
    }
    return count;
}

bool NonBlockingActuator::IsKeyHeld(uint8_t vk) const {
    for (const auto& slot : m_activeSlots) {
        if (slot.active && slot.type == ActuatorActionType::KeyboardVk && slot.vkCode == vk) {
            return true;
        }
    }
    return false;
}

void NonBlockingActuator::ReleaseSlot(const ScheduledInputEvent& slot) {
    switch (slot.type) {
        case ActuatorActionType::KeyboardVk:
            m_kmbox.KeyUpVk(slot.vkCode);
            break;
        case ActuatorActionType::KeyboardHid:
            m_kmbox.KeyUp(slot.hidCode);
            break;
        case ActuatorActionType::MouseButton:
            m_kmbox.MouseUp(slot.mouseButton);
            break;
        default:
            break;
    }
}

bool NonBlockingActuator::RegisterRelease(ActuatorActionType type, uint8_t vk, uint8_t hid, int btn, uint64_t releaseMs) {
    // Nếu phím này đã tồn tại trong hàng đợi, gia hạn thời gian release
    for (auto& slot : m_activeSlots) {
        if (slot.active && slot.type == type) {
            if ((type == ActuatorActionType::KeyboardVk && slot.vkCode == vk) ||
                (type == ActuatorActionType::KeyboardHid && slot.hidCode == hid) ||
                (type == ActuatorActionType::MouseButton && slot.mouseButton == btn)) {
                slot.releaseTimestampMs = releaseMs;
                return true;
            }
        }
    }

    // Tìm slot trống đầu tiên
    for (auto& slot : m_activeSlots) {
        if (!slot.active) {
            slot.type = type;
            slot.vkCode = vk;
            slot.hidCode = hid;
            slot.mouseButton = btn;
            slot.releaseTimestampMs = releaseMs;
            slot.active = true;
            return true;
        }
    }

    // Nếu đầy cả 16 slot: lập tức nhả phím để tránh kẹt
    ScheduledInputEvent overflowSlot;
    overflowSlot.type = type;
    overflowSlot.vkCode = vk;
    overflowSlot.hidCode = hid;
    overflowSlot.mouseButton = btn;
    ReleaseSlot(overflowSlot);
    return false;
}

} // namespace input
