// ============================================================================
// INPUT MANAGER IMPLEMENTATION
// ============================================================================
// Processes touch and button inputs into InputEvent objects

#include "input_manager.h"
#include "pico/stdlib.h"
#include "board_config.h"
#include <stdio.h>

// External interrupt flags from basic1.cpp
extern volatile bool touch_interrupt_flag;
extern volatile bool button_key0_pressed;
extern volatile bool button_key1_pressed;

// GameConfig struct definition (matches basic1.cpp)
struct GameConfig {
    uint32_t touch_debounce_ms;
    uint32_t button_debounce_ms;
    bool enable_gestures;
    bool enable_continuous_draw;
    bool debug_verbose;
};

InputManager::InputManager(LowLevelTouch* touch, const GameConfig* config)
    : touch(touch), config(config) {
}

bool InputManager::has_touch() const {
    return touch != nullptr;
}

bool InputManager::has_buttons() const {
#ifdef BUTTON_KEY0_PIN
    return true;
#else
    return false;
#endif
}

InputEvent InputManager::process_touch_input(uint32_t* last_time) {
    InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
    
    // Process immediately on IRQ, and continue sampling while a touch is active.
    // Some controllers only IRQ on edge transitions, so move events require polling.
    if (!touch_interrupt_flag && *last_time == 0) {
        return event;  // No touch event
    }

    // Always validate via controller state instead of relying on edge flag alone.
    // Edge chatter can flip touch_event_down without a real touch transition.
    TouchData touch_data;
    if (!touch || !touch->read_touch(&touch_data)) {
        touch_interrupt_flag = false;
        return event;
    }

    touch_interrupt_flag = false;
    uint32_t now = to_ms_since_boot(get_absolute_time());

    // No active touch points: require consecutive empty reads before release to
    // avoid false TOUCH_UP events from transient controller jitter.
    if (touch_data.touch_count == 0) {
        if (*last_time != 0) {
            no_touch_samples++;
            if (no_touch_samples >= 2) {
                *last_time = 0;
                no_touch_samples = 0;
                event.type = INPUT_TOUCH_UP;
                event.valid = true;
            }
        }
        return event;
    }
    no_touch_samples = 0;

    // Debounce touch-down/move updates.
    if (*last_time != 0 && (now - *last_time) < config->touch_debounce_ms) {
        return event;
    }

    // Populate event from first touch point.
    event.x = touch_data.points[0].x;
    event.y = touch_data.points[0].y;
    event.pressure = touch_data.points[0].pressure;
    event.gesture_code = touch_data.gesture;
    event.valid = true;

    if (*last_time == 0) {
        event.type = INPUT_TOUCH_DOWN;

        InputType virtual_type;
        if (check_virtual_buttons(event.x, event.y, virtual_type)) {
            event.type = virtual_type;
            event.button_id = (virtual_type == INPUT_BUTTON_0) ? 0 : 1;
            if (config->debug_verbose) {
                printf("Virtual button %d pressed via touch\n", event.button_id);
            }
        }
    } else {
        event.type = INPUT_TOUCH_MOVE;
    }

    if (config->enable_gestures && touch_data.gesture != 0) {
        event.type = INPUT_GESTURE;
        if (config->debug_verbose) {
            printf("Gesture: 0x%02X (%s)\n", event.gesture_code, get_gesture_name(event.gesture_code));
        }
    }

    *last_time = now;
    return event;
}

InputEvent InputManager::process_button_input() {
    InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
    
#ifdef BUTTON_KEY0_PIN
    // Check KEY0
    if (button_key0_pressed) {
        button_key0_pressed = false;
        sleep_ms(config->button_debounce_ms);
        
        if (gpio_get(BUTTON_KEY0_PIN) == 0) {  // Verify still pressed
            event.type = INPUT_BUTTON_0;
            event.button_id = 0;
            event.valid = true;
            
            if (config->debug_verbose) {
                printf("Button KEY0 action triggered\n");
            }
            return event;
        }
    }
    
#ifdef BUTTON_KEY1_PIN
    // Check KEY1
    if (button_key1_pressed) {
        button_key1_pressed = false;
        sleep_ms(config->button_debounce_ms);
        
        if (gpio_get(BUTTON_KEY1_PIN) == 0) {  // Verify still pressed
            event.type = INPUT_BUTTON_1;
            event.button_id = 1;
            event.valid = true;
            
            if (config->debug_verbose) {
                printf("Button KEY1 action triggered\n");
            }
            return event;
        }
    }
#endif
#endif
    
    return event;
}

const char* InputManager::get_gesture_name(uint8_t gesture_code) {
    switch(gesture_code) {
        case 0x10: return "Move Up";
        case 0x14: return "Move Right";
        case 0x18: return "Move Down";
        case 0x1C: return "Move Left";
        case 0x48: return "Zoom In";
        case 0x49: return "Zoom Out";
        default: return "Unknown";
    }
}

void InputManager::get_virtual_button_regions(int* a_rect, int* b_rect) const {
    for (int i = 0; i < 4; i++) {
        a_rect[i] = v_button_a[i];
        b_rect[i] = v_button_b[i];
    }
}

void InputManager::set_virtual_button_regions(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) {
    v_button_a[0] = ax; v_button_a[1] = ay; v_button_a[2] = aw; v_button_a[3] = ah;
    v_button_b[0] = bx; v_button_b[1] = by; v_button_b[2] = bw; v_button_b[3] = bh;
    v_buttons_active = true;
}

void InputManager::clear_virtual_button_regions() {
    v_buttons_active = false;
}

bool InputManager::check_virtual_buttons(int16_t x, int16_t y, InputType& out_type) const {
    if (!v_buttons_active) return false;

    if (x >= v_button_a[0] && x <= v_button_a[0] + v_button_a[2] && 
        y >= v_button_a[1] && y <= v_button_a[1] + v_button_a[3]) {
        out_type = INPUT_BUTTON_0;
        return true;
    }
    
    if (x >= v_button_b[0] && x <= v_button_b[0] + v_button_b[2] && 
        y >= v_button_b[1] && y <= v_button_b[1] + v_button_b[3]) {
        out_type = INPUT_BUTTON_1;
        return true;
    }
    
    return false;
}
