#include "app.h"
#include "world_manager.h"

#include <algorithm>
#include <stdexcept>
#include <string>

void App::framebufferResizeCallback(GLFWwindow* window, int, int) {
    auto* app = reinterpret_cast<App*>(glfwGetWindowUserPointer(window));
    if (app) {
        app->framebufferResized_ = true;
    }
}

void App::mouseCallback(GLFWwindow* window, double xpos, double ypos) {
    auto* app = reinterpret_cast<App*>(glfwGetWindowUserPointer(window));
    if (!app) return;

    // Only rotate camera when actively playing and inventory is closed
    if (app->gameState_ != GameState::Playing || app->inventoryOpen_) {
        app->firstMouse_ = true;
        return;
    }

    if (app->firstMouse_) {
        app->lastMouseX_ = xpos;
        app->lastMouseY_ = ypos;
        app->firstMouse_ = false;
    }

    double xoffset = xpos - app->lastMouseX_;
    double yoffset = ypos - app->lastMouseY_;

    app->lastMouseX_ = xpos;
    app->lastMouseY_ = ypos;

    app->camera_.processMouse(xoffset, yoffset);
}

void App::scrollCallback(GLFWwindow* window, double /*xoffset*/, double yoffset) {
    auto* app = reinterpret_cast<App*>(glfwGetWindowUserPointer(window));
    if (!app) return;

    if (app->gameState_ == GameState::Playing) {
        if (app->inventoryOpen_) {
            // Scroll does nothing when inventory is open
        } else if (app->flyModeActive_) {
            if (yoffset > 0.0) app->flySpeedLevel_ = std::min(app->flySpeedLevel_ + 1, 3);
            if (yoffset < 0.0) app->flySpeedLevel_ = std::max(app->flySpeedLevel_ - 1, 0);
            app->updateWindowTitle();
        } else {
            if (yoffset > 0.0) app->hotbarSlot_ = (app->hotbarSlot_ + 3) % 4;
            if (yoffset < 0.0) app->hotbarSlot_ = (app->hotbarSlot_ + 1) % 4;
        }
    } else if (app->gameState_ == GameState::MainMenu) {
        // Scroll through world list
        if (app->menuSubState_ == 0 && app->worldList_.size() > 0) {
            int n = static_cast<int>(app->worldList_.size());
            if (yoffset > 0.0) app->menuSel_ = std::max(0, app->menuSel_ - 1);
            if (yoffset < 0.0) app->menuSel_ = std::min(n - 1, app->menuSel_ + 1);
        }
    }
}

void App::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    (void)scancode; (void)mods;
    auto* app = reinterpret_cast<App*>(glfwGetWindowUserPointer(window));
    if (!app) return;

    if (action == GLFW_PRESS || action == GLFW_REPEAT) {
        if (key == GLFW_KEY_BACKSPACE) {
            if (app->gameState_ == GameState::MainMenu && app->menuSubState_ == 1) {
                if (app->textField_ == 0 && !app->nameInput_.empty())
                    app->nameInput_.pop_back();
                else if (app->textField_ == 1 && !app->seedInput_.empty())
                    app->seedInput_.pop_back();
            } else if (app->gameState_ == GameState::MainMenu && app->menuSubState_ == 2) {
                if (!app->nameInput_.empty())
                    app->nameInput_.pop_back();
            }
        }
    }

    if (action == GLFW_PRESS) {
        if (key == GLFW_KEY_ESCAPE) {
            if (app->gameState_ == GameState::Playing) {
                // Pause
                app->gameState_ = GameState::Paused;
                glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
            } else if (app->gameState_ == GameState::Paused) {
                // Resume
                app->gameState_ = GameState::Playing;
                glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
                app->firstMouse_ = true;
            } else if (app->gameState_ == GameState::MainMenu) {
                if (app->menuSubState_ == 1) {
                    app->menuSubState_ = 0;
                    app->nameInput_.clear();
                    app->seedInput_.clear();
                    app->textField_ = 0;
                } else if (app->menuSubState_ == 2) {
                    if (app->deleteConfirm_) {
                        app->deleteConfirm_ = 0;
                    } else {
                        app->menuSubState_ = 0;
                        app->nameInput_.clear();
                        app->textField_ = 0;
                        app->settingsWorldSel_ = -1;
                    }
                }
            }
        }

        if (key == GLFW_KEY_ENTER || key == GLFW_KEY_KP_ENTER) {
            if (app->gameState_ == GameState::MainMenu && app->menuSubState_ == 2) {
                if (!app->nameInput_.empty() && !app->deleteConfirm_
                    && app->settingsWorldSel_ >= 0
                    && app->settingsWorldSel_ < static_cast<int>(app->worldList_.size())) {
                    std::string oldName = app->worldList_[app->settingsWorldSel_].name;
                    if (WorldManager::renameWorld(oldName, app->nameInput_)) {
                        app->worldList_ = WorldManager::listWorlds();
                        app->menuSubState_ = 0;
                        app->nameInput_.clear();
                        app->textField_ = 0;
                        app->settingsWorldSel_ = -1;
                    }
                }
            } else if (app->gameState_ == GameState::MainMenu && app->menuSubState_ == 1) {
                if (app->textField_ == 0 && !app->nameInput_.empty()) {
                    // Move focus to seed field
                    app->textField_ = 1;
                } else if (app->textField_ == 1 || !app->nameInput_.empty()) {
                    // Confirm world creation
                    uint64_t seed = 12345ULL;
                    if (!app->seedInput_.empty()) {
                        try { seed = std::stoull(app->seedInput_, nullptr, 16); } catch (...) {}
                    } else {
                        // Random seed
                        seed = static_cast<uint64_t>(std::chrono::steady_clock::now()
                                                      .time_since_epoch().count());
                    }
                    if (WorldManager::createWorld(app->nameInput_, seed)) {
                        app->worldList_ = WorldManager::listWorlds();
                        WorldInfo wi{app->nameInput_, seed};
                        app->nameInput_.clear();
                        app->seedInput_.clear();
                        app->menuSubState_ = 0;
                        app->textField_    = 0;
                        app->startWorld(wi);
                    }
                }
            }
        }

        if (key == GLFW_KEY_TAB) {
            if (app->gameState_ == GameState::MainMenu && app->menuSubState_ == 1)
                app->textField_ = 1 - app->textField_; // toggle between name / seed
        }

        // Inventory toggle
        if (key == GLFW_KEY_E && app->gameState_ == GameState::Playing) {
            app->inventoryOpen_ = !app->inventoryOpen_;
            app->audio_.playUIClick();
            if (app->inventoryOpen_) {
                glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
                app->firstMouse_ = true;
            } else {
                glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
                app->firstMouse_ = true;
                // Return held item to inventory if any
                if (app->heldItem_.type != BlockType::Air) {
                    for (int i = 0; i < 16 && app->heldItem_.count > 0; ++i) {
                        auto& s = app->inventory_[i];
                        if (s.type == app->heldItem_.type && s.count < 256) {
                            int take = std::min(256 - s.count, app->heldItem_.count);
                            s.count += take; app->heldItem_.count -= take;
                        }
                    }
                    for (int i = 0; i < 16 && app->heldItem_.count > 0; ++i) {
                        auto& s = app->inventory_[i];
                        if (s.type == BlockType::Air) { s = app->heldItem_; app->heldItem_ = {}; break; }
                    }
                    app->heldItem_ = {};
                }
            }
        }

        // Hotbar selection 1-4
        if (app->gameState_ == GameState::Playing) {
            if (key >= GLFW_KEY_1 && key <= GLFW_KEY_4)
                app->hotbarSlot_ = key - GLFW_KEY_1;
        }
    }
}

void App::charCallback(GLFWwindow* window, unsigned int codepoint) {
    auto* app = reinterpret_cast<App*>(glfwGetWindowUserPointer(window));
    if (!app) return;

    if (app->gameState_ == GameState::MainMenu && app->menuSubState_ == 1) {
        char c = static_cast<char>(codepoint);
        if (app->textField_ == 0) {
            // Name: letters, digits, hyphen, space — max 12 chars
            bool ok = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
                   || (c >= '0' && c <= '9') || c == '-' || c == ' ';
            if (ok && static_cast<int>(app->nameInput_.size()) < 12)
                app->nameInput_ += c;
        } else {
            // Seed: hex chars — max 16 chars
            bool ok = (c >= '0' && c <= '9')
                   || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
            if (ok && static_cast<int>(app->seedInput_.size()) < 16)
                app->seedInput_ += static_cast<char>(std::toupper(c));
        }
    } else if (app->gameState_ == GameState::MainMenu && app->menuSubState_ == 2) {
        char c = static_cast<char>(codepoint);
        bool ok = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
               || (c >= '0' && c <= '9') || c == '-' || c == ' ';
        if (ok && static_cast<int>(app->nameInput_.size()) < 12)
            app->nameInput_ += c;
    }
}

void App::initWindow() {
    if (!glfwInit()) {
        throw std::runtime_error("glfwInit failed");
    }

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

    // Use 80% of the primary monitor for a comfortable windowed size
    GLFWmonitor* monitor = glfwGetPrimaryMonitor();
    const GLFWvidmode* mode = monitor ? glfwGetVideoMode(monitor) : nullptr;
    int winW = mode ? mode->width  * 4 / 5 : static_cast<int>(WINDOW_WIDTH);
    int winH = mode ? mode->height * 4 / 5 : static_cast<int>(WINDOW_HEIGHT);

    window_ = glfwCreateWindow(winW, winH, "minecraft", nullptr, nullptr);

    if (!window_) {
        throw std::runtime_error("glfwCreateWindow failed");
    }

    glfwSetWindowUserPointer(window_, this);
    glfwSetFramebufferSizeCallback(window_, framebufferResizeCallback);
    glfwSetCursorPosCallback(window_, mouseCallback);
    glfwSetScrollCallback(window_, scrollCallback);
    glfwSetKeyCallback(window_, keyCallback);
    glfwSetCharCallback(window_, charCallback);
    // Start with cursor free for main menu; captured when game starts
    glfwSetInputMode(window_, GLFW_CURSOR, GLFW_CURSOR_NORMAL);

    updateWindowTitle();
}
