#include "lumacs/gtk_window_controller.hpp"
#include "lumacs/editor_core.hpp"
#include "lumacs/lua_api.hpp"
#include "lumacs/minibuffer_manager.hpp" // For MinibufferMode
#include <algorithm> // For std::max, std::min
#include <iostream>

namespace lumacs {

GtkWindowController::GtkWindowController(EditorCore& core, GtkRenderer& renderer,
                                         std::shared_ptr<Window> window, Gtk::DrawingArea& drawing_area)
    : core_(core), renderer_(renderer), window_(window), drawing_area_(drawing_area) {
}

void GtkWindowController::connect_events() {
    // Add input handling  
    auto controller = Gtk::EventControllerKey::create();
    controller->signal_key_pressed().connect(
        sigc::mem_fun(*this, &GtkWindowController::on_key_pressed), false);
    drawing_area_.add_controller(controller);
    
    // Add click handling to set active window explicitly and move cursor
    auto click_controller = Gtk::GestureClick::create();
    click_controller->signal_pressed().connect(
        sigc::mem_fun(*this, &GtkWindowController::on_click_pressed));
    drawing_area_.add_controller(click_controller);

    // Add Drag Gesture for Selection
    auto drag_controller = Gtk::GestureDrag::create();
    drag_controller->signal_drag_begin().connect(
        sigc::mem_fun(*this, &GtkWindowController::on_drag_begin));
    drag_controller->signal_drag_update().connect(
        sigc::mem_fun(*this, &GtkWindowController::on_drag_update));
    drawing_area_.add_controller(drag_controller);
    
    // Add scroll handling
    auto scroll_controller = Gtk::EventControllerScroll::create();
    scroll_controller->set_flags(Gtk::EventControllerScroll::Flags::VERTICAL);
    scroll_controller->signal_scroll().connect(
        sigc::mem_fun(*this, &GtkWindowController::on_scroll), true);
    drawing_area_.add_controller(scroll_controller);
}

void GtkWindowController::set_cached_active_window(std::shared_ptr<Window> window) {
    cached_active_window_ = window;
}

void GtkWindowController::set_cursor_visible(bool visible) {
    cursor_visible_ = visible;
}


bool GtkWindowController::on_key_pressed(guint keyval, guint /*keycode*/, Gdk::ModifierType state) {
    if (!window_) return false;

    // Make cursor visible immediately when typing
    cursor_visible_ = true;
    drawing_area_.queue_draw(); // Redraw immediately to show cursor

    // 1. Convert GDK keyval and state to our canonical Key representation
    std::string key_name;
    // Special handling for some keys
    switch (keyval) {
        case GDK_KEY_Return: key_name = "Return"; break;
        case GDK_KEY_Tab: key_name = "Tab"; break;
        case GDK_KEY_Escape: key_name = "Escape"; break;
        case GDK_KEY_BackSpace: key_name = "Backspace"; break;
        case GDK_KEY_Delete: key_name = "Delete"; break;
        case GDK_KEY_Up: key_name = "ArrowUp"; break;
        case GDK_KEY_Down: key_name = "ArrowDown"; break;
        case GDK_KEY_Left: key_name = "ArrowLeft"; break;
        case GDK_KEY_Right: key_name = "ArrowRight"; break;
        case GDK_KEY_Home: key_name = "Home"; break;
        case GDK_KEY_End: key_name = "End"; break;
        case GDK_KEY_Page_Up: key_name = "PageUp"; break;
        case GDK_KEY_Page_Down: key_name = "PageDown"; break;
        case GDK_KEY_F3: key_name = "F3"; break;
        case GDK_KEY_F4: key_name = "F4"; break;
        default:
            // Handle printable characters
            if (keyval >= GDK_KEY_a && keyval <= GDK_KEY_z) {
                key_name = std::string(1, static_cast<char>(keyval));
            } else if (keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) {
                key_name = std::string(1, static_cast<char>(keyval));
            } else if (keyval >= 32 && keyval <= 126) { 
                key_name = std::string(1, static_cast<char>(keyval));
            }
            break;
    }

    if (key_name.empty()) return false; // Unhandled key

    // Apply modifiers
    unsigned int state_uint = static_cast<unsigned int>(state);
    if (state_uint & static_cast<unsigned int>(Gdk::ModifierType::CONTROL_MASK)) {
        key_name = "C-" + key_name;
    }
    if (state_uint & static_cast<unsigned int>(Gdk::ModifierType::ALT_MASK) ||
        state_uint & static_cast<unsigned int>(Gdk::ModifierType::META_MASK)) {
        key_name = "M-" + key_name;
    }
    if (state_uint & static_cast<unsigned int>(Gdk::ModifierType::SHIFT_MASK)) {
        key_name = "S-" + key_name;
    }

    // 2. Handle Minibuffer Input Logic (Command/Buffer/File modes)
    if (core_.minibuffer_manager().is_active()) {
        // Pass the key event to the MinibufferManager
        core_.minibuffer_manager().handle_key_event(key_name);
        drawing_area_.queue_draw();
        return true;
    }

    // 3. Normal Mode Processing (Pass to KeyBindingManager)
    KeyProcessingResult result = core_.keybinding_manager().process_key(key_name);

    if (result.command_result.has_value()) {
        core_.set_message(result.command_result->message);
    }

    drawing_area_.queue_draw();
    return result.type != KeyResult::Unbound; // Return true if handled, false otherwise
}


void GtkWindowController::on_click_pressed(int /*n_press*/, double x, double y) {
    if (!window_ || !cached_active_window_) return;

    // 1. Activate Window
    if (core_.active_window() != window_) {
        core_.set_active_window(window_);
        cached_active_window_ = window_;
    }
    // IMPORTANT: Grab keyboard focus for this widget
    drawing_area_.grab_focus();
    
    // 2. Move Cursor
    if (auto pos = renderer_.resolve_screen_pos(window_, x, y)) {
        window_->set_cursor(*pos);
        // Clear mark on simple click
        window_->buffer().deactivate_mark();
        drawing_area_.queue_draw();
    }
}

void GtkWindowController::on_drag_begin(double x, double y) {
    if (!window_ || !cached_active_window_) return;

    if (auto pos = renderer_.resolve_screen_pos(window_, x, y)) {
        // Set mark at start of drag
        window_->buffer().set_mark(*pos);
        window_->set_cursor(*pos);
        drawing_area_.queue_draw();
    }
}

void GtkWindowController::on_drag_update(double dx, double dy) {
    if (!window_ || !cached_active_window_) return;

    // TODO: Get start point from drag_controller
    // double start_x, start_y;
    // if (drag_controller->get_start_point(start_x, start_y)) {
    //      double current_x = start_x + dx;
    //      double current_y = start_y + dy;
         
    //      if (auto pos = renderer_.resolve_screen_pos(window_, current_x, current_y)) {
    //          window_->set_cursor(*pos);
    //          drawing_area_.queue_draw();
    //      }
    // }
    drawing_area_.queue_draw();
}

bool GtkWindowController::on_scroll(double /*dx*/, double dy) {
    if (!window_) return false;

    // dy is usually 1.0 or -1.0 for wheel steps
    // Scroll 3 lines per step
    int lines = static_cast<int>(dy * 3.0);
    if (lines != 0) {
        window_->scroll_lines(lines);
        drawing_area_.queue_draw();
    }
    return true;
}

} // namespace lumacs