#include "glfw_window.h"
#include "glfw_input.h"
#include <extra2d/utils/logger.h>
#include <glad/glad.h>

namespace extra2d {

GLFWWindow::GLFWWindow() {}

GLFWWindow::~GLFWWindow() {
    destroy();
}

bool GLFWWindow::create(const std::string& title, int width, int height, bool vsync) {
    if (!initGLFW()) {
        return false;
    }

    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
    glfwWindowHint(GLFW_DEPTH_BITS, 24);
    glfwWindowHint(GLFW_STENCIL_BITS, 8);

#ifdef __SWITCH__
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
    glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
    fullscreen_ = true;
#else
    glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
#endif

    GLFWmonitor* monitor = nullptr;
#ifdef __SWITCH__
    monitor = glfwGetPrimaryMonitor();
#endif

    glfwWindow_ = glfwCreateWindow(
        width, height,
        title.c_str(),
        monitor,
        nullptr
    );

    if (!glfwWindow_) {
        E2D_LOG_ERROR("Failed to create GLFW window");
        deinitGLFW();
        return false;
    }

#ifndef __SWITCH__
    if (!fullscreen_ && !monitor) {
        GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
        if (primaryMonitor) {
            const GLFWvidmode* mode = glfwGetVideoMode(primaryMonitor);
            if (mode) {
                int screenWidth = mode->width;
                int screenHeight = mode->height;
                int windowX = (screenWidth - width) / 2;
                int windowY = (screenHeight - height) / 2;
                glfwSetWindowPos(glfwWindow_, windowX, windowY);
            }
        }
    }
#endif

    glfwMakeContextCurrent(glfwWindow_);

    if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) {
        E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
        glfwDestroyWindow(glfwWindow_);
        glfwWindow_ = nullptr;
        deinitGLFW();
        return false;
    }

    glfwSwapInterval(vsync ? 1 : 0);
    vsync_ = vsync;

    glfwGetWindowSize(glfwWindow_, &width_, &height_);
    updateContentScale();

    glfwSetWindowUserPointer(glfwWindow_, this);
    glfwSetFramebufferSizeCallback(glfwWindow_, framebufferSizeCallback);
    glfwSetWindowCloseCallback(glfwWindow_, windowCloseCallback);
    glfwSetWindowFocusCallback(glfwWindow_, windowFocusCallback);
    glfwSetWindowIconifyCallback(glfwWindow_, windowIconifyCallback);
    glfwSetCursorPosCallback(glfwWindow_, cursorPosCallback);
    glfwSetMouseButtonCallback(glfwWindow_, mouseButtonCallback);
    glfwSetScrollCallback(glfwWindow_, scrollCallback);
    glfwSetKeyCallback(glfwWindow_, keyCallback);
    glfwSetJoystickCallback(joystickCallback);

    input_ = makeUnique<GLFWInput>();
    input_->setWindow(glfwWindow_);
    input_->init();

    E2D_LOG_INFO("GLFW window created: {}x{}", width_, height_);
    E2D_LOG_INFO("  Platform: OpenGL ES 3.2");
    return true;
}

void GLFWWindow::destroy() {
    if (input_) {
        input_->shutdown();
        input_.reset();
    }

    if (glfwWindow_) {
        glfwDestroyWindow(glfwWindow_);
        glfwWindow_ = nullptr;
    }

    deinitGLFW();
}

void GLFWWindow::poll() {
    if (!glfwWindow_) return;

    if (input_) {
        input_->update();
    }

    glfwPollEvents();
}

void GLFWWindow::swap() {
    if (glfwWindow_) {
        glfwSwapBuffers(glfwWindow_);
    }
}

bool GLFWWindow::shouldClose() const {
    if (!glfwWindow_) return true;
    return shouldClose_ || glfwWindowShouldClose(glfwWindow_);
}

void GLFWWindow::close() {
    shouldClose_ = true;
    if (glfwWindow_) {
        glfwSetWindowShouldClose(glfwWindow_, GLFW_TRUE);
    }
}

void GLFWWindow::setTitle(const std::string& title) {
    if (glfwWindow_) {
        glfwSetWindowTitle(glfwWindow_, title.c_str());
    }
}

void GLFWWindow::setSize(int w, int h) {
    if (glfwWindow_) {
        glfwSetWindowSize(glfwWindow_, w, h);
        width_ = w;
        height_ = h;
    }
}

void GLFWWindow::setPos(int x, int y) {
#ifndef __SWITCH__
    if (glfwWindow_) {
        glfwSetWindowPos(glfwWindow_, x, y);
    }
#else
    (void)x;
    (void)y;
#endif
}

void GLFWWindow::setFullscreen(bool fs) {
#ifndef __SWITCH__
    if (!glfwWindow_) return;
    
    if (fs == fullscreen_) return;
    
    if (fs) {
        GLFWmonitor* monitor = glfwGetPrimaryMonitor();
        const GLFWvidmode* mode = glfwGetVideoMode(monitor);
        glfwSetWindowMonitor(glfwWindow_, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
    } else {
        glfwSetWindowMonitor(glfwWindow_, nullptr, 100, 100, 1280, 720, 0);
    }
    fullscreen_ = fs;
    glfwGetWindowSize(glfwWindow_, &width_, &height_);
    updateContentScale();
#else
    (void)fs;
#endif
}

void GLFWWindow::setVSync(bool vsync) {
    if (glfwWindow_) {
        glfwSwapInterval(vsync ? 1 : 0);
        vsync_ = vsync;
    }
}

void GLFWWindow::setVisible(bool visible) {
#ifndef __SWITCH__
    if (glfwWindow_) {
        if (visible) {
            glfwShowWindow(glfwWindow_);
        } else {
            glfwHideWindow(glfwWindow_);
        }
    }
#else
    (void)visible;
#endif
}

int GLFWWindow::width() const {
    return width_;
}

int GLFWWindow::height() const {
    return height_;
}

Size GLFWWindow::size() const {
    return Size(static_cast<float>(width_), static_cast<float>(height_));
}

Vec2 GLFWWindow::pos() const {
    int x = 0, y = 0;
#ifndef __SWITCH__
    if (glfwWindow_) {
        glfwGetWindowPos(glfwWindow_, &x, &y);
    }
#endif
    return Vec2(static_cast<float>(x), static_cast<float>(y));
}

bool GLFWWindow::fullscreen() const {
    return fullscreen_;
}

bool GLFWWindow::vsync() const {
    return vsync_;
}

bool GLFWWindow::focused() const {
    return focused_;
}

bool GLFWWindow::minimized() const {
    return minimized_;
}

float GLFWWindow::scaleX() const {
    return scaleX_;
}

float GLFWWindow::scaleY() const {
    return scaleY_;
}

void GLFWWindow::setCursor(Cursor cursor) {
#ifndef __SWITCH__
    if (!glfwWindow_) return;

    if (cursor == Cursor::Hidden) {
        glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
        return;
    }

    glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_NORMAL);

    GLFWcursor* glfwCursor = nullptr;
    switch (cursor) {
        case Cursor::Arrow:
            glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
            break;
        case Cursor::IBeam:
            glfwCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
            break;
        case Cursor::Crosshair:
            glfwCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR);
            break;
        case Cursor::Hand:
            glfwCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
            break;
        case Cursor::HResize:
            glfwCursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
            break;
        case Cursor::VResize:
            glfwCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
            break;
        default:
            glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
            break;
    }

    if (glfwCursor) {
        glfwSetCursor(glfwWindow_, glfwCursor);
    }
#else
    (void)cursor;
#endif
}

void GLFWWindow::showCursor(bool show) {
#ifndef __SWITCH__
    if (glfwWindow_) {
        glfwSetInputMode(glfwWindow_, GLFW_CURSOR, show ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
        cursorVisible_ = show;
    }
#else
    (void)show;
#endif
}

void GLFWWindow::lockCursor(bool lock) {
#ifndef __SWITCH__
    if (glfwWindow_) {
        glfwSetInputMode(glfwWindow_, GLFW_CURSOR, lock ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
        cursorLocked_ = lock;
    }
#else
    (void)lock;
#endif
}

IInput* GLFWWindow::input() const {
    return input_.get();
}

void GLFWWindow::onResize(ResizeCb cb) {
    resizeCb_ = cb;
}

void GLFWWindow::onClose(CloseCb cb) {
    closeCb_ = cb;
}

void GLFWWindow::onFocus(FocusCb cb) {
    focusCb_ = cb;
}

void* GLFWWindow::native() const {
    return glfwWindow_;
}

bool GLFWWindow::initGLFW() {
    static int glfwInitCount = 0;
    if (glfwInitCount == 0) {
        if (!glfwInit()) {
            E2D_LOG_ERROR("Failed to initialize GLFW");
            return false;
        }
        glfwInitCount++;
    }
    return true;
}

void GLFWWindow::deinitGLFW() {
    static int glfwInitCount = 1;
    glfwInitCount--;
    if (glfwInitCount == 0) {
        glfwTerminate();
    }
}

void GLFWWindow::updateContentScale() {
    if (glfwWindow_) {
        int fbWidth, fbHeight;
        glfwGetFramebufferSize(glfwWindow_, &fbWidth, &fbHeight);
        scaleX_ = fbWidth > 0 ? static_cast<float>(fbWidth) / width_ : 1.0f;
        scaleY_ = fbHeight > 0 ? static_cast<float>(fbHeight) / height_ : 1.0f;
    }
}

// 静态回调函数
void GLFWWindow::framebufferSizeCallback(GLFWwindow* window, int width, int height) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self) {
        self->width_ = width;
        self->height_ = height;
        self->updateContentScale();
        if (self->resizeCb_) {
            self->resizeCb_(width, height);
        }
    }
}

void GLFWWindow::windowCloseCallback(GLFWwindow* window) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self) {
        self->shouldClose_ = true;
        if (self->closeCb_) {
            self->closeCb_();
        }
    }
}

void GLFWWindow::windowFocusCallback(GLFWwindow* window, int focused) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self) {
        self->focused_ = (focused == GLFW_TRUE);
        if (self->focusCb_) {
            self->focusCb_(self->focused_);
        }
    }
}

void GLFWWindow::windowIconifyCallback(GLFWwindow* window, int iconified) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self) {
        self->minimized_ = (iconified == GLFW_TRUE);
    }
}

void GLFWWindow::cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self && self->input_) {
        self->input_->handleCursorPosEvent(xpos, ypos);
    }
}

void GLFWWindow::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self && self->input_) {
        self->input_->handleMouseButtonEvent(button, action, mods);
    }
}

void GLFWWindow::scrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self && self->input_) {
        self->input_->handleScrollEvent(xoffset, yoffset);
    }
}

void GLFWWindow::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
    if (self && self->input_) {
        self->input_->handleKeyEvent(key, scancode, action, mods);
    }
}

void GLFWWindow::joystickCallback(int jid, int event) {
    // 通过全局回调找到对应的窗口实例
    // 由于 GLFW 的 joystick 回调没有窗口参数，我们需要其他方式处理
    // 这里简化处理，让输入系统在 update() 中轮询
}

} // namespace extra2d
