#include "InputManager.h"
#include <SDL3/SDL.h>
#include <algorithm>

InputManager::InputManager() {
    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "InputManager initialized");
}

void InputManager::processEvents() {
    // Update previous state before processing new events
    updateInputState();

    // Reset mouse delta
    m_mouseDeltaX = 0.0f;
    m_mouseDeltaY = 0.0f;

    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        // Trace every polled event type for debugging abrupt termination
        {
            FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "InputManager: polled event type=%d\n", (int)event.type); fclose(f); }
        }
        switch (event.type) {
            case SDL_EVENT_QUIT:
            case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
                handleQuitEvent();
                break;

            case SDL_EVENT_KEY_DOWN:
            case SDL_EVENT_KEY_UP:
                handleKeyEvent(event.key);
                break;

            case SDL_EVENT_MOUSE_BUTTON_DOWN:
            case SDL_EVENT_MOUSE_BUTTON_UP:
                handleMouseButtonEvent(event.button);
                break;

            case SDL_EVENT_MOUSE_MOTION:
                handleMouseMotionEvent(event.motion);
                break;

            case SDL_EVENT_WINDOW_RESIZED:
            case SDL_EVENT_WINDOW_MOVED:
            case SDL_EVENT_WINDOW_MINIMIZED:
            case SDL_EVENT_WINDOW_MAXIMIZED:
            case SDL_EVENT_WINDOW_RESTORED:
                handleWindowEvent(event.window);
                break;

            default:
                // Unhandled event types
                break;
        }
    }
}

void InputManager::update(float deltaTime) {
    updateDAS(deltaTime);
}

bool InputManager::isKeyPressed(SDL_Scancode key) const {
    auto current = m_currentKeyState.find(key);
    auto previous = m_previousKeyState.find(key);
    
    bool currentlyPressed = (current != m_currentKeyState.end() && current->second);
    bool previouslyPressed = (previous != m_previousKeyState.end() && previous->second);
    
    return currentlyPressed && !previouslyPressed;
}

bool InputManager::isKeyReleased(SDL_Scancode key) const {
    auto current = m_currentKeyState.find(key);
    auto previous = m_previousKeyState.find(key);
    
    bool currentlyPressed = (current != m_currentKeyState.end() && current->second);
    bool previouslyPressed = (previous != m_previousKeyState.end() && previous->second);
    
    return !currentlyPressed && previouslyPressed;
}

bool InputManager::isKeyHeld(SDL_Scancode key) const {
    auto it = m_currentKeyState.find(key);
    return (it != m_currentKeyState.end() && it->second);
}

bool InputManager::isMouseButtonPressed(int button) const {
    auto current = m_currentMouseState.find(button);
    auto previous = m_previousMouseState.find(button);
    
    bool currentlyPressed = (current != m_currentMouseState.end() && current->second);
    bool previouslyPressed = (previous != m_previousMouseState.end() && previous->second);
    
    return currentlyPressed && !previouslyPressed;
}

bool InputManager::isMouseButtonReleased(int button) const {
    auto current = m_currentMouseState.find(button);
    auto previous = m_previousMouseState.find(button);
    
    bool currentlyPressed = (current != m_currentMouseState.end() && current->second);
    bool previouslyPressed = (previous != m_previousMouseState.end() && previous->second);
    
    return !currentlyPressed && previouslyPressed;
}

bool InputManager::isMouseButtonHeld(int button) const {
    auto it = m_currentMouseState.find(button);
    return (it != m_currentMouseState.end() && it->second);
}

void InputManager::getMousePosition(float& x, float& y) const {
    x = m_mouseX;
    y = m_mouseY;
}

void InputManager::getMouseDelta(float& deltaX, float& deltaY) const {
    deltaX = m_mouseDeltaX;
    deltaY = m_mouseDeltaY;
}

void InputManager::registerKeyHandler(KeyHandler handler) {
    m_keyHandlers.push_back(std::move(handler));
}

void InputManager::registerMouseButtonHandler(MouseButtonHandler handler) {
    m_mouseButtonHandlers.push_back(std::move(handler));
}

void InputManager::registerMouseMotionHandler(MouseMotionHandler handler) {
    m_mouseMotionHandlers.push_back(std::move(handler));
}

void InputManager::registerWindowEventHandler(WindowEventHandler handler) {
    m_windowEventHandlers.push_back(std::move(handler));
}

void InputManager::registerQuitHandler(QuitHandler handler) {
    m_quitHandlers.push_back(std::move(handler));
}

void InputManager::configureDAS(float delayMs, float repeatMs) {
    m_dasDelay = delayMs;
    m_dasRepeat = repeatMs;
    SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "DAS configured: delay=%.1fms, repeat=%.1fms", delayMs, repeatMs);
}

bool InputManager::checkDASMovement(SDL_Scancode leftKey, SDL_Scancode rightKey, int& direction) {
    bool leftHeld = isKeyHeld(leftKey);
    bool rightHeld = isKeyHeld(rightKey);

    // Determine current direction
    int currentDirection = 0;
    if (leftHeld && !rightHeld) {
        currentDirection = -1;
    } else if (rightHeld && !leftHeld) {
        currentDirection = 1;
    }

    // If no direction or direction changed, reset DAS
    if (currentDirection == 0 || 
        (m_dasState.isActive && 
         ((currentDirection == -1 && m_dasState.activeKey != leftKey) ||
          (currentDirection == 1 && m_dasState.activeKey != rightKey)))) {
        resetDAS();
        direction = 0;
        return false;
    }

    // Check for initial key press (immediate movement)
    if ((currentDirection == -1 && isKeyPressed(leftKey)) ||
        (currentDirection == 1 && isKeyPressed(rightKey))) {
        m_dasState.isActive = true;
        m_dasState.delayTimer = m_dasDelay;
        m_dasState.repeatTimer = 0.0f;
        m_dasState.activeKey = (currentDirection == -1) ? leftKey : rightKey;
        direction = currentDirection;
        return true;
    }

    // If DAS is active and delay/repeat timers expired, trigger movement
    if (m_dasState.isActive && m_dasState.delayTimer <= 0.0f && m_dasState.repeatTimer <= 0.0f) {
        m_dasState.repeatTimer = m_dasRepeat;
        direction = currentDirection;
        return true;
    }

    direction = 0;
    return false;
}

void InputManager::handleKeyEvent(const SDL_KeyboardEvent& event) {
    SDL_Scancode scancode = event.scancode;
    bool pressed = (event.type == SDL_EVENT_KEY_DOWN) && !event.repeat;
    bool released = (event.type == SDL_EVENT_KEY_UP);

    if (pressed) {
        m_currentKeyState[scancode] = true;
    } else if (released) {
        m_currentKeyState[scancode] = false;
    }

    // Notify handlers
    for (auto& handler : m_keyHandlers) {
        try {
            handler(scancode, pressed);
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in key handler: %s", e.what());
        }
    }
}

void InputManager::handleMouseButtonEvent(const SDL_MouseButtonEvent& event) {
    int button = event.button;
    bool pressed = (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN);
    
    m_currentMouseState[button] = pressed;

    // Notify handlers
    for (auto& handler : m_mouseButtonHandlers) {
        try {
            handler(button, pressed, event.x, event.y);
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in mouse button handler: %s", e.what());
        }
    }
}

void InputManager::handleMouseMotionEvent(const SDL_MouseMotionEvent& event) {
    float newX = event.x;
    float newY = event.y;
    
    m_mouseDeltaX = newX - m_mouseX;
    m_mouseDeltaY = newY - m_mouseY;
    m_mouseX = newX;
    m_mouseY = newY;

    // Notify handlers
    for (auto& handler : m_mouseMotionHandlers) {
        try {
            handler(m_mouseX, m_mouseY, m_mouseDeltaX, m_mouseDeltaY);
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in mouse motion handler: %s", e.what());
        }
    }
}

void InputManager::handleWindowEvent(const SDL_WindowEvent& event) {
    if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
        handleQuitEvent();
    }

    // Notify handlers
    for (auto& handler : m_windowEventHandlers) {
        try {
            handler(event);
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in window event handler: %s", e.what());
        }
    }
}

void InputManager::updateInputState() {
    // Copy current state to previous state
    m_previousKeyState = m_currentKeyState;
    m_previousMouseState = m_currentMouseState;
}

void InputManager::updateDAS(float deltaTime) {
    if (!m_dasState.isActive) {
        return;
    }

    float deltaMs = deltaTime * 1000.0f; // Convert to milliseconds

    // Update delay timer
    if (m_dasState.delayTimer > 0.0f) {
        m_dasState.delayTimer -= deltaMs;
    }

    // Update repeat timer (only if delay has expired)
    if (m_dasState.delayTimer <= 0.0f && m_dasState.repeatTimer > 0.0f) {
        m_dasState.repeatTimer -= deltaMs;
    }
}

void InputManager::resetDAS() {
    m_dasState.isActive = false;
    m_dasState.delayTimer = 0.0f;
    m_dasState.repeatTimer = 0.0f;
    m_dasState.activeKey = SDL_SCANCODE_UNKNOWN;
}

// IInputHandler interface implementation
bool InputManager::processEvent(const SDL_Event& event) {
    // Process individual event and return if it was handled
    switch (event.type) {
        case SDL_EVENT_KEY_DOWN:
        case SDL_EVENT_KEY_UP:
            handleKeyEvent(event.key);
            return true;
        case SDL_EVENT_MOUSE_BUTTON_DOWN:
        case SDL_EVENT_MOUSE_BUTTON_UP:
            handleMouseButtonEvent(event.button);
            return true;
        case SDL_EVENT_MOUSE_MOTION:
            handleMouseMotionEvent(event.motion);
            return true;
        case SDL_EVENT_WINDOW_RESIZED:
        case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
        case SDL_EVENT_WINDOW_MINIMIZED:
        case SDL_EVENT_WINDOW_RESTORED:
            handleWindowEvent(event.window);
            return true;
        case SDL_EVENT_QUIT:
            handleQuitEvent();
            return true;
        default:
            return false;
    }
}

void InputManager::update(double deltaTime) {
    update(static_cast<float>(deltaTime));
}

bool InputManager::isKeyCurrentlyPressed(SDL_Scancode scancode) const {
    return isKeyHeld(scancode);
}

bool InputManager::isKeyJustPressed(SDL_Scancode scancode) const {
    return isKeyPressed(scancode);
}

bool InputManager::isKeyJustReleased(SDL_Scancode scancode) const {
    return isKeyReleased(scancode);
}

bool InputManager::isQuitRequested() const {
    return shouldQuit();
}

void InputManager::reset() {
    // Clear pressed/released states, keep held states
    // In the current InputManager implementation, we use previous/current state
    // so we just copy current to previous to reset the "just pressed/released" states
    m_previousKeyState = m_currentKeyState;
    m_previousMouseState = m_currentMouseState;
}

void InputManager::handleQuitEvent() {
    FILE* f = fopen("spacetris_trace.log", "a");
    if (f) {
        fprintf(f, "InputManager::handleQuitEvent invoked\n");
        fclose(f);
    }
    m_shouldQuit = true;
    for (auto& handler : m_quitHandlers) {
        handler();
    }
}
