// GLFW 版本的渲染入口：画触觉面板盒子 + 点阵实例化圆点，
// 右键拖拽环绕相机，滚轮调 FOV，内置简单波纹数据做演示。
#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "myshader.hh"
#include "stb_image_wrap.h"

struct AppState {
    // 点阵规格
    int rows = 8;
    int cols = 10;
    float pitch = 0.025f;
    float dotRadius = 0.008f;
    // 面板尺寸
    float panelW = 0.25f;
    float panelH = 0.35f;
    float panelD = 0.01f;
    // 数据范围
    int minV = 0;
    int maxV = 100;
    // 渲染模式：0=有光照，1=无光照（flat/unlit）
    int renderMode = 1;

    // 相机状态（环绕）
    float camYaw = -90.0f;
    float camPitch = 0.0f;
    float zoom = 45.0f;
    bool rightDown = false;
    double lastMouseX = 0.0;
    double lastMouseY = 0.0;

    bool valuesDirty = true;
    std::vector<float> values;

    glm::mat4 mvp{1.0f};
    glm::vec3 cameraPos{0.0f, 0.0f, 3.0f};
};

AppState g_state;

Shader* bg_shader = nullptr;
Shader* room_shader = nullptr;
Shader* panel_shader = nullptr;
Shader* dots_shader = nullptr;

unsigned int panel_vao = 0;
unsigned int panel_vbo = 0;
unsigned int panel_ibo = 0;
int panel_index_count = 0;

unsigned int room_vao = 0;
unsigned int room_vbo = 0;
unsigned int room_ibo = 0;
int room_index_count = 0;

unsigned int dots_vao = 0;
unsigned int dots_vbo = 0;
unsigned int instance_vbo = 0;
int instance_count = 0;

unsigned int dot_tex = 0;

unsigned int bg_vao = 0;
unsigned int bg_vbo = 0;

unsigned int metal_tex = 0;
bool panel_geometry_dirty = true;
bool dot_geometry_dirty = true;

void framebuffer_size_callback(GLFWwindow* /*window*/, int width, int height) {
    // 视口随窗口大小变化
    glViewport(0, 0, width, height);
}

void mouse_button_callback(GLFWwindow* window, int button, int action, int /*mods*/) {
    // 右键按下/抬起，用于开启/关闭环绕相机拖拽
    if (button == GLFW_MOUSE_BUTTON_RIGHT) {
        if (action == GLFW_PRESS) {
            g_state.rightDown = true;
            glfwGetCursorPos(window, &g_state.lastMouseX, &g_state.lastMouseY);
        } else if (action == GLFW_RELEASE) {
            g_state.rightDown = false;
        }
    }
}

void cursor_pos_callback(GLFWwindow* /*window*/, double xpos, double ypos) {
    // 右键拖拽时，根据鼠标增量更新 yaw/pitch
    if (!g_state.rightDown)
        return;

    const float dx = static_cast<float>(xpos - g_state.lastMouseX);
    const float dy = static_cast<float>(ypos - g_state.lastMouseY);
    g_state.lastMouseX = xpos;
    g_state.lastMouseY = ypos;

    g_state.camYaw += dx * 0.3f;
    if (g_state.camYaw >= -70) {
        g_state.camYaw = -70;
    }
    if (g_state.camYaw <= -110) {
        g_state.camYaw = -110;
    }
    g_state.camPitch = std::clamp(g_state.camPitch + dy * 0.3f, -20.0f, 20.0f);
}

void scroll_callback(GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) {
    // 滚轮调整视角 FOV（越小越近）
    const float factor = std::pow(0.9f, static_cast<float>(yoffset));
    g_state.zoom = std::clamp(g_state.zoom * factor, 5.0f, 80.0f);
}

void process_input(GLFWwindow* window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
        glfwSetWindowShouldClose(window, true);
    }
}

GLFWwindow* glfw_init() {
    // 初始化 GLFW + GLAD，创建窗口与上下文
    if (!glfwInit()) {
        std::cerr << "Failed to init GLFW" << std::endl;
        return nullptr;
    }
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#if __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
    GLFWwindow* window = glfwCreateWindow(800, 600, "Tactile Module Test (GLFW)", nullptr, nullptr);
    if (window == nullptr) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return nullptr;
    }
    glfwMakeContextCurrent(window);
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cerr << "Failed to initialize GLAD" << std::endl;
        glfwDestroyWindow(window);
        glfwTerminate();
        return nullptr;
    }

    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetCursorPosCallback(window, cursor_pos_callback);
    glfwSetScrollCallback(window, scroll_callback);

    glViewport(0, 0, 800, 600);
    glfwSwapInterval(1);
    return window;
}

int dot_count() {
    // 当前点阵总数
    return g_state.rows * g_state.cols;
}

void set_panel_size(float w, float h, float d) {
    g_state.panelW = w;
    g_state.panelH = h;
    g_state.panelD = d;
    panel_geometry_dirty = true;
}

void set_spec(int rows, int cols, float pitch, float dotRadius) {
    // 设置点阵规格，同时自动计算面板宽/深以留出圆点边界
    g_state.rows = std::max(0, rows);
    g_state.cols = std::max(0, cols);
    g_state.pitch = std::max(0.0f, pitch);
    g_state.dotRadius = std::max(0.0f, dotRadius);

    const float gridW = static_cast<float>(std::max(0, g_state.cols - 1)) * g_state.pitch;
    const float gridD = static_cast<float>(std::max(0, g_state.rows - 1)) * g_state.pitch;
    g_state.panelW = gridW + 2.0f * g_state.dotRadius;
    g_state.panelH = gridD + 2.0f * g_state.dotRadius;
    panel_geometry_dirty = true;
    dot_geometry_dirty = true;

    g_state.values.assign(dot_count(), static_cast<float>(g_state.minV));
    g_state.valuesDirty = true;
}

void init_background_geometry() {
    // 背景网格：全屏二三角形，顶点坐标直接在 NDC 空间
    if (bg_vbo) {
        glDeleteBuffers(1, &bg_vbo);
        bg_vbo = 0;
    }
    if (bg_vao) {
        glDeleteVertexArrays(1, &bg_vao);
        bg_vao = 0;
    }

    const float verts[] = {
        -1.0f, -1.0f,
         1.0f, -1.0f,
         1.0f,  1.0f,
        -1.0f, -1.0f,
         1.0f,  1.0f,
        -1.0f,  1.0f,
    };

    glGenVertexArrays(1, &bg_vao);
    glBindVertexArray(bg_vao);

    glGenBuffers(1, &bg_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, bg_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);

    glBindVertexArray(0);
}

void init_room_geometry() {
    // 房间：一个“倒扣的大盒子”（立方体），我们从里面看，所以法线朝向盒子内部
    if (room_ibo) {
        glDeleteBuffers(1, &room_ibo);
        room_ibo = 0;
    }
    if (room_vbo) {
        glDeleteBuffers(1, &room_vbo);
        room_vbo = 0;
    }
    if (room_vao) {
        glDeleteVertexArrays(1, &room_vao);
        room_vao = 0;
    }

    struct V {
        float x, y, z;
        float nx, ny, nz;
    };

    // 单位立方体：顶点范围 [-1, 1]，真正房间大小在 room.vert 里通过 uRoomHalfSize 缩放
    // 下面的法线是“朝内”的（方便在房间内部打光）
    V verts[24] = {
        // floor (y = -1), normal +Y
        {-1, -1, -1,  0, +1,  0},
        {+1, -1, -1,  0, +1,  0},
        {+1, -1, +1,  0, +1,  0},
        {-1, -1, +1,  0, +1,  0},

        // ceiling (y = +1), normal -Y
        {-1, +1, +1,  0, -1,  0},
        {+1, +1, +1,  0, -1,  0},
        {+1, +1, -1,  0, -1,  0},
        {-1, +1, -1,  0, -1,  0},

        // back wall (z = -1), normal +Z
        {-1, +1, -1,  0,  0, +1},
        {+1, +1, -1,  0,  0, +1},
        {+1, -1, -1,  0,  0, +1},
        {-1, -1, -1,  0,  0, +1},

        // front wall (z = +1), normal -Z
        {+1, +1, +1,  0,  0, -1},
        {-1, +1, +1,  0,  0, -1},
        {-1, -1, +1,  0,  0, -1},
        {+1, -1, +1,  0,  0, -1},

        // left wall (x = -1), normal +X
        {-1, +1, +1, +1,  0,  0},
        {-1, +1, -1, +1,  0,  0},
        {-1, -1, -1, +1,  0,  0},
        {-1, -1, +1, +1,  0,  0},

        // right wall (x = +1), normal -X
        {+1, +1, -1, -1,  0,  0},
        {+1, +1, +1, -1,  0,  0},
        {+1, -1, +1, -1,  0,  0},
        {+1, -1, -1, -1,  0,  0},
    };

    unsigned int idx[36] = {
        0, 1, 2,  0, 2, 3,      // floor
        4, 5, 6,  4, 6, 7,      // ceiling
        8, 9, 10, 8, 10, 11,    // back
        12, 13, 14, 12, 14, 15, // front
        16, 17, 18, 16, 18, 19, // left
        20, 21, 22, 20, 22, 23  // right
    };
    room_index_count = 36;

    glGenVertexArrays(1, &room_vao);
    glBindVertexArray(room_vao);

    glGenBuffers(1, &room_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, room_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);

    glGenBuffers(1, &room_ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, room_ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW);

    // layout 和 panel 一样：
    // location 0: position
    // location 1: normal
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(V), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(V), (void*)(3 * sizeof(float)));

    glBindVertexArray(0);
}

void init_panel_geometry() {
    // 面板盒子：VAO+VBO+IBO，包含顶面/底面/侧面 6 个面
    if (panel_ibo) {
        glDeleteBuffers(1, &panel_ibo);
        panel_ibo = 0;
    }
    if (panel_vbo) {
        glDeleteBuffers(1, &panel_vbo);
        panel_vbo = 0;
    }
    if (panel_vao) {
        glDeleteVertexArrays(1, &panel_vao);
        panel_vao = 0;
    }

    const float y = g_state.panelH * 0.5f;
    const float hw = g_state.panelW * 0.5f;
    const float hd = g_state.panelD * 0.5f;

    struct V {
        float x, y, z;
        float nx, ny, nz;
    };

    V verts[24] = {
        {-hw, +y, -hd,  0, +1,  0},
        {+hw, +y, -hd,  0, +1,  0},
        {+hw, +y, +hd,  0, +1,  0},
        {-hw, +y, +hd,  0, +1,  0},
        {-hw, +y, +hd,  0,  0, +1},
        {+hw, +y, +hd,  0,  0, +1},
        {+hw, -y, +hd,  0,  0, +1},
        {-hw, -y, +hd,  0,  0, +1},
        {-hw, -y, +hd,  0, -1,  0},
        {+hw, -y, +hd,  0, -1,  0},
        {+hw, -y, -hd,  0, -1,  0},
        {-hw, -y, -hd,  0, -1,  0},
        {+hw, +y, -hd,  0,  0, -1},
        {-hw, +y, -hd,  0,  0, -1},
        {-hw, -y, -hd,  0,  0, -1},
        {+hw, -y, -hd,  0,  0, -1},
        {-hw, +y, -hd, -1,  0,  0},
        {-hw, +y, +hd, -1,  0,  0},
        {-hw, -y, +hd, -1,  0,  0},
        {-hw, -y, -hd, -1,  0,  0},
        {+hw, +y, +hd, +1,  0,  0},
        {+hw, +y, -hd, +1,  0,  0},
        {+hw, -y, -hd, +1,  0,  0},
        {+hw, -y, +hd, +1,  0,  0},
    };

    unsigned int idx[36] = {
        0, 1, 2,  0, 2, 3,
        4, 5, 6,  4, 6, 7,
        8, 9, 10, 8, 10, 11,
        12, 13, 14, 12, 14, 15,
        16, 17, 18, 16, 18, 19,
        20, 21, 22, 20, 22, 23
    };
    panel_index_count = 36;

    glGenVertexArrays(1, &panel_vao);
    glBindVertexArray(panel_vao);

    glGenBuffers(1, &panel_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, panel_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);

    glGenBuffers(1, &panel_ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, panel_ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(V), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(V), (void*)(3 * sizeof(float)));
    glBindVertexArray(0);
}

void init_dot_geometry() {
    // 圆点：基于一个单位 quad，使用 instanced attributes 传位置/值
    if (instance_vbo) {
        glDeleteBuffers(1, &instance_vbo);
        instance_vbo = 0;
    }
    if (dots_vbo) {
        glDeleteBuffers(1, &dots_vbo);
        dots_vbo = 0;
    }
    if (dots_vao) {
        glDeleteVertexArrays(1, &dots_vao);
        dots_vao = 0;
    }

    struct V {
        float x, y;
        float u, v;
    };

    V quad[6] = {
        {-1, -1, 0, 0},
        { 1, -1, 1, 0},
        { 1,  1, 1, 1},
        {-1, -1, 0, 0},
        { 1,  1, 1, 1},
        {-1,  1, 0, 1},
    };

    glGenVertexArrays(1, &dots_vao);
    glBindVertexArray(dots_vao);

    glGenBuffers(1, &dots_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, dots_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(V), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(V), (void*)(2 * sizeof(float)));

    glGenBuffers(1, &instance_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, instance_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * std::max(1, dot_count()), nullptr, GL_DYNAMIC_DRAW);

    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glVertexAttribDivisor(2, 1);

    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(2 * sizeof(float)));
    glVertexAttribDivisor(3, 1);

    glBindVertexArray(0);
}

// void init_dot_texture() {
//     // 简单 4x4 RGBA 程序化纹理，模拟金属纹理感
//     if (dot_tex) {
//         glDeleteTextures(1, &dot_tex);
//         dot_tex = 0;
//     }

//     // Simple procedural 4x4 texture to mimic a brushed metal feel.
//     const unsigned char pixels[] = {
//         180, 175, 170, 255, 185, 180, 175, 255, 190, 185, 180, 255, 185, 180, 175, 255,
//         185, 180, 175, 255, 190, 185, 180, 255, 195, 190, 185, 255, 190, 185, 180, 255,
//         190, 185, 180, 255, 195, 190, 185, 255, 200, 195, 190, 255, 195, 190, 185, 255,
//         185, 180, 175, 255, 190, 185, 180, 255, 195, 190, 185, 255, 190, 185, 180, 255,
//     };

//     glGenTextures(1, &dot_tex);
//     glBindTexture(GL_TEXTURE_2D, dot_tex);
//     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
//     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_LINEAR_MIPMAP_LINEAR);
//     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_LINEAR_MIPMAP_LINEGL_LINEARAR);
//     glPixelStorei(GL_UNPACK_ALIGNMENT, 1GL_LINEARGL_CLAMP_TO_EDGE     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 40, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
//     glGenerateMipmap(GL_TEXTURE_GL_CLAMP_TO_EDGE2D
// glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//     glBindTexture(GL_TEXTURE_2D, 0);
// }

void init_programs() {
    // 加载并编译 shader program
    const std::string vsd_path = "../shaders/dots.vert";
    const std::string fsd_path = "../shaders/dots.frag";
    glBindTexture(GL_TEXTURE_2D, 0);
    const std::string vsb_path = "../shaders/bg.vert";
    const std::string fsb_path = "../shaders/bg.frag";
    const std::string vsr_path = "../shaders/room.vert";
    const std::string fsr_path = "../shaders/room.frag";
    const std::string vsp_path = "../shaders/panel.vert";
    const std::string fsp_path = "../shaders/panel.frag";

    bg_shader = new Shader(vsb_path.c_str(), fsb_path.c_str());
    room_shader = new Shader(vsr_path.c_str(), fsr_path.c_str());
    dots_shader = new Shader(vsd_path.c_str(), fsd_path.c_str());
    panel_shader = new Shader(vsp_path.c_str(), fsp_path.c_str());
}

void update_instance_buffer_if_needed() {
    // 如果有新数据，重新填充实例缓冲：每个点 3 个 float (x,z,value)
    if (dot_count() <= 0) {
        instance_count = 0;
        return;
    }
    if (!g_state.valuesDirty)
        return;

    const int n = dot_count();
    instance_count = n;
    std::vector<float> inst;
    inst.resize(n * 3);

    const float w = static_cast<float>(std::max(0, g_state.cols - 1)) * g_state.pitch;
    const float h = static_cast<float>(std::max(0, g_state.rows - 1)) * g_state.pitch;
    for (int i = 0; i < n; ++i) {
        const int r = (g_state.cols > 0) ? (i / g_state.cols) : 0;
        const int c = (g_state.cols > 0) ? (i % g_state.cols) : 0;

        const float x = (static_cast<float>(c) * g_state.pitch) - w * 0.5f;
        const float y = (static_cast<float>(r) * g_state.pitch) - h * 0.5f;
        inst[i * 3 + 0] = x;
        inst[i * 3 + 1] = y;
        inst[i * 3 + 2] = (i < static_cast<int>(g_state.values.size())) ? g_state.values[i] : static_cast<float>(g_state.minV);
    }

    glBindBuffer(GL_ARRAY_BUFFER, instance_vbo);
    glBufferSubData(GL_ARRAY_BUFFER, 0, inst.size() * sizeof(float), inst.data());
    g_state.valuesDirty = false;
}

void update_matrices(int fbWidth, int fbHeight) {
    // 计算 MVP：透视投影 * 观察（环绕相机）* 单位模型
    if (fbWidth <= 0 || fbHeight <= 0) {
        g_state.mvp = glm::mat4(1.0f);
        return;
    }

    const float aspect = static_cast<float>(fbWidth) / static_cast<float>(fbHeight);
    const float radius = 0.5f * std::sqrt(g_state.panelW * g_state.panelW + g_state.panelD * g_state.panelD);
    const float distance = std::max(0.5f, radius * 2.5f);

    // 目标点：面板中心
    const glm::vec3 center(0.0f, 0.0f, g_state.panelD*0.5f);
    const float yawRad = glm::radians(g_state.camYaw);
    const float pitchRad = glm::radians(g_state.camPitch);
    const float cosPitch = std::cos(pitchRad);

    const glm::vec3 eye = center + glm::vec3(
        distance * cosPitch * std::cos(yawRad),
        distance * std::sin(pitchRad),
        distance * cosPitch * std::sin(yawRad)
    );
    g_state.cameraPos = eye;

    // 构建视图矩阵与透视矩阵
    const glm::mat4 view = glm::lookAt(eye, center, glm::vec3(0.0f, 1.0f, 0.0f));
    const glm::mat4 proj = glm::perspective(glm::radians(g_state.zoom), aspect, 0.01f, std::max(10.0f, distance * 10.0f));
    g_state.mvp = proj * view;
}

void render_background(int fbWidth, int fbHeight) {
    // 屏幕空间网格，不受相机旋转影响
    if (!bg_shader || !bg_vao) return;

    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);

    bg_shader->use();
    bg_shader->setVec2("uViewport", glm::vec2(static_cast<float>(fbWidth), static_cast<float>(fbHeight)));
    bg_shader->setFloat("uMinorStep", 24.0f);
    bg_shader->setFloat("uMajorStep", 120.0f);

    glBindVertexArray(bg_vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBindVertexArray(0);

    glDepthMask(GL_TRUE);
    glEnable(GL_DEPTH_TEST);
}

void render_room() {
    // 3D 房间背景：用一个大盒子把场景包起来（相当于“屋子墙壁/地面”）
    if (!room_shader || !room_vao)
        return;

    // 房间尺寸：根据面板尺寸做一个“够大”的包围盒（单位：世界坐标）
    const float base = std::max({g_state.panelW, g_state.panelH, g_state.panelD});
    const glm::vec3 roomHalfSize(
        std::max(1.0f, base * 1.1f), // X: 左右墙离中心的距离
        std::max(1.0f, base * 1.1f), // Y: 地面/天花板离中心的距离
        std::max(1.0f, base * 1.1f)  // Z: 前后墙离中心的距离
    );

    // 网格尺寸（让房间更有空间感）
    const float minorStep = std::max(0.05f, base * 0.25f);
    const float majorStep = minorStep * 5.0f;

    room_shader->use();
    room_shader->setMat4("uMVP", g_state.mvp);
    room_shader->setVec3("uCameraPos", g_state.cameraPos);
    room_shader->setVec3("uRoomHalfSize", roomHalfSize);
    room_shader->setFloat("uMinorStep", minorStep);
    room_shader->setFloat("uMajorStep", majorStep);
    room_shader->setInt("uRenderMode", g_state.renderMode);

    glBindVertexArray(room_vao);
    glDrawElements(GL_TRIANGLES, room_index_count, GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(0);
}

void render_panel() {
    // 绘制面板盒子
    if (!panel_shader || !panel_vao) return;

    panel_shader->use();
    panel_shader->setMat4("uMVP", g_state.mvp);
    panel_shader->setVec3("uCameraPos", g_state.cameraPos);
    panel_shader->setFloat("uPanelW", g_state.panelW);
    panel_shader->setFloat("uPanelH", g_state.panelH);
    panel_shader->setFloat("uPanelD", g_state.panelD);
    panel_shader->setInt("uRows", g_state.rows);
    panel_shader->setInt("uCols", g_state.cols);
    panel_shader->setFloat("uPitch", g_state.pitch);
    panel_shader->setFloat("uDotRadius", g_state.dotRadius);
    panel_shader->setInt("uRenderMode", g_state.renderMode);

    glBindVertexArray(panel_vao);
    glDrawElements(GL_TRIANGLES, panel_index_count, GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(0);
}

void render_dots() {
    // 实例化绘制圆点
    if (!dots_shader || !dots_vao || instance_count <= 0) return;

    dots_shader->use();
    dots_shader->setMat4("uMVP", g_state.mvp);
    dots_shader->setInt("uRenderMode", g_state.renderMode);
    dots_shader->setFloat("uDotRadius", g_state.dotRadius);
    dots_shader->setFloat("uBaseZ", -(g_state.panelD * 0.5f) - 0.001f);
    dots_shader->setFloat("uMinV", static_cast<float>(g_state.minV));
    dots_shader->setFloat("uMaxV", static_cast<float>(g_state.maxV));
    dots_shader->setInt("uHasData", 1);
    dots_shader->setVec3("uCameraPos", g_state.cameraPos);
    dots_shader->setInt("uDotTex", 0);
    if (dot_tex) {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, dot_tex);
    }

    glBindVertexArray(dots_vao);
    glDrawArraysInstanced(GL_TRIANGLES, 0, 6, instance_count);
    glBindVertexArray(0);

    if (dot_tex) {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
}

void update_demo_values(float t) {
    // 生成演示用波纹数据，保持实例缓冲在动
    const int n = dot_count();
    if (n <= 0) {
        instance_count = 0;
        return;
    }
    if (static_cast<int>(g_state.values.size()) != n) {
        g_state.values.assign(n, static_cast<float>(g_state.minV));
    }
    for (int i = 0; i < n; ++i) {
        const float phase = t * 0.6f + static_cast<float>(i) * 0.35f;
        const float wave = 0.5f + 0.5f * std::sin(phase);
        const float minV = static_cast<float>(g_state.minV);
        const float maxV = static_cast<float>(g_state.maxV);
        g_state.values[i] = minV + (maxV - minV) * wave;
    }
    g_state.valuesDirty = true;
}

void destroy_context() {
    delete bg_shader;
    delete room_shader;
    delete panel_shader;
    delete dots_shader;
    if (room_vao) glDeleteVertexArrays(1, &room_vao);
    if (room_vbo) glDeleteBuffers(1, &room_vbo);
    if (room_ibo) glDeleteBuffers(1, &room_ibo);
    if (panel_vao) glDeleteVertexArrays(1, &panel_vao);
    if (panel_vbo) glDeleteBuffers(1, &panel_vbo);
    if (panel_ibo) glDeleteBuffers(1, &panel_ibo);
    if (dots_vao) glDeleteVertexArrays(1, &dots_vao);
    if (dots_vbo) glDeleteBuffers(1, &dots_vbo);
    if (instance_vbo) glDeleteBuffers(1, &instance_vbo);
    if (bg_vao) glDeleteVertexArrays(1, &bg_vao);
    if (bg_vbo) glDeleteBuffers(1, &bg_vbo);
    if (dot_tex) glDeleteTextures(1, &dot_tex);
}

void load_metal_texture(const std::string& path) {
    if (metal_tex) {
        glDeleteTextures(1, &metal_tex);
        metal_tex = 0;
    }
    glGenTextures(1, &metal_tex);
    glBindTexture(GL_TEXTURE_2D, metal_tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    int width, height, channels;
    unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, 0);
    if (data) {
        std::cout << "load texture image path: " << path << std::endl;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    stbi_image_free(data);
}


int main() {
    // 初始化窗口/上下文
    GLFWwindow* window = glfw_init();
    if (window == nullptr) {
        return -1;
    }

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    // 初始化规格与资源
    set_spec(8, 10, 0.025f, 0.008f);
    init_programs();
    // init_dot_texture();
    init_room_geometry();
    load_metal_texture("../images/metal.jpeg");
    while (!glfwWindowShouldClose(window)) {
        const float currentFrame = static_cast<float>(glfwGetTime());
        process_input(window);

        int fbWidth = 0, fbHeight = 0;
        glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
        glViewport(0, 0, fbWidth, fbHeight);
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        if (panel_geometry_dirty) {
            init_panel_geometry();
            panel_geometry_dirty = false;
        }
        if (dot_geometry_dirty) {
            init_dot_geometry();
            dot_geometry_dirty = false;
            g_state.valuesDirty = true;
        }

        update_matrices(fbWidth, fbHeight);
        update_demo_values(currentFrame);
        update_instance_buffer_if_needed();

        render_room();
        render_panel();
        render_dots();

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    destroy_context();
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}
