
// Created by Lenn on 2025/12/16.
//

#ifndef TACTILEIPC3D_GLWIDGET_H
#define TACTILEIPC3D_GLWIDGET_H

#include <qopenglshaderprogram.h>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QMutex>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions_3_3_Core>
#include <QMatrix4x4>
#include <QVector3D>
#include <QString>
#include <QColor>
#include <QRectF>
#include <atomic>
#include <qtmetamacros.h>
#include <qvectornd.h>
#include <QVector2D>

struct Ray {
    QVector3D origin;
    QVector3D dir;
};

class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core {
    Q_OBJECT
    Q_PROPERTY(float yaw READ yaw WRITE setYaw NOTIFY yawChanged)
    Q_PROPERTY(int row READ row WRITE setRow)
    Q_PROPERTY(int col READ col WRITE setCol)
    // Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid NOTIFY showGridChanged)
public:
    enum RenderMode {
        Realistic = 0,
        DataViz = 1,
    };
    Q_ENUM(RenderMode)

    enum LabelMode {
        LabelsOff = 0,
        LabelsHover = 1,
        LabelsAlways = 2,
    };
    Q_ENUM(LabelMode)

    explicit GLWidget(QWidget* parent = nullptr);
    ~GLWidget();

    // panel 的物理尺寸（单位随你：米/毫米都行，只要全程一致）
    void setPanelSize(float w, float h, float d);
    void setPanelThickness(float h);
    // rows/cols: 传感点阵行列；pitch: 点与点的间距；dotRaius: 单个点的“显示半径”
    void setSpec(int rows, int cols, float pitch, float dotRaius);
    // 总点数 = 行 * 列
    int dotCount() const { return m_cols * m_rows; }

    // 提交一帧传感值（数量必须等于 dotCount），在 paintGL 里用 instanced 的方式绘制
    void submitValues(const QVector<float>& values);

    float yaw() const { return m_camYawDeg; }

    bool showGrid() const { return m_showGrid; }

    int row() const { return m_rows; }    
    int col() const { return m_cols; }

public slots:
    // 值域范围，用于 shader 里把 value 映射到颜色（绿->红）
    void setRange(int minV, int maxV);
    void setYaw(float yawDeg);
    void setRenderModeString(const QString& mode);
    void setLabelModeString(const QString& mode);
    void setLightMode(bool on);
    void setShowBg(bool on);
    void setShowGrid(bool on);
    void setCol(int col);
    void setRow(int row);
    void setColorLow(const QColor& color);
    void setColorMid(const QColor& color);
    void setColorHigh(const QColor& color);

signals:
    void yawChanged();
    void dotClicked(int index, int row, int col, float value);

protected:
    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;

private:
    void initGeometry_();
    void initPanelGeometry_();
    void initDotGeometry_();
    void initBackgroundGeometry_();
    void initPrograms_();
    void initDotTexture_();
    void initRoomGeometry_();
    void initHeatmapGeometry_();
    void initBaseGeometry_();
    void initSkirtGeometry_();
    void initHeightTexture_();
    void updateInstanceBufferIfNeeded_();
    void updateMatrices_();
    void updateRoom_();
    void updateHeatmapData_();
    void uploadHeightTexture_();
    void updateSkirtVertices_();
    bool projectToScreen_(const QVector3D& world, QPointF* out) const;
    int pickDotIndex_(const QPoint& pos, QVector3D* worldOut) const;
    float sampleHeightValue_(float u, float v) const;
    void renderHeatmap_();
    void renderBase_();
    void renderSkirt_();
    void drawToggleButton_(QPainter* painter);
private:
    // 传感值范围（用于颜色映射）
    int m_min = 0;
    int m_max = 1000;

    // 点阵规格
    int m_rows = 3;
    int m_cols = 4;

    // panel: 一个长方体/板子（当前只画顶面矩形）
    float m_panelW = 0.25f;
    float m_panelH = 0.35f;
    float m_panelD = 0.05f;

    // 点阵布局参数
    float m_pitch = 0.1f;
    float m_dotRadius = 0.03f;

    // 传感数据（GUI/定时器线程提交，GL 线程绘制）
    QMutex m_dataMutex;
    QVector<float> m_latestValues;
    bool m_valuesDirty = false;

    std::atomic<bool> m_hasData{false};

    // shader program（编译/链接后的可执行 GPU 程序）
    QOpenGLShaderProgram* m_bgProg = nullptr;
    QOpenGLShaderProgram* m_roomProg = nullptr;
    QOpenGLShaderProgram* m_panelProg = nullptr;
    QOpenGLShaderProgram* m_dotsProg = nullptr;
    QOpenGLShaderProgram* m_heatmapProg = nullptr;
    QOpenGLShaderProgram* m_baseProg = nullptr;

    unsigned int m_dotTex = 0;

    // panel 的 VAO/VBO/IBO（VAO: 顶点格式/绑定状态；VBO: 顶点数据；IBO: 索引数据）
    unsigned int m_panelVao = 0;
    unsigned int m_panelVbo = 0;
    unsigned int m_panelIbo = 0;
    int m_panelIndexCount = 0;
    bool m_panelGeometryDirty = false;

    // dots 的 VAO/VBO + instance VBO（instance VBO 每个点一条：offsetXZ + value）
    unsigned int m_dotsVao = 0;
    unsigned int m_dotsVbo = 0;
    unsigned int m_instanceVbo = 0;

    unsigned int m_heatmapVao = 0;
    unsigned int m_heatmapVbo = 0;
    unsigned int m_heatmapIbo = 0;
    int m_heatmapIndexCount = 0;
    int m_heatmapCols = 0;
    int m_heatmapRows = 0;

    unsigned int m_baseVao = 0;
    unsigned int m_baseVbo = 0;
    unsigned int m_baseIbo = 0;
    int m_baseIndexCount = 0;

    unsigned int m_skirtVao = 0;
    unsigned int m_skirtVbo = 0;
    unsigned int m_skirtIbo = 0;
    int m_skirtIndexCount = 0;
    QVector<QVector2D> m_skirtUVs;
    QVector<QVector3D> m_skirtNormals;
    QVector<float> m_skirtVertices;

    unsigned int m_heightTex = 0;
    int m_heightW = 0;
    int m_heightH = 0;
    int m_heightTexW = 0;
    int m_heightTexH = 0;
    QVector<float> m_heightValues;
    bool m_heightDirty = false;

    bool m_useHeatmap = false;
    QRectF m_toggleRect;
    int m_upscale = 100;
    float m_dentMax = 0.08f;
    
    unsigned int room_vao = 0;
    unsigned int room_vbo = 0;
    unsigned int room_ibo = 0;
    int room_index_count = 0;
    bool m_showGrid = true;
    
    int m_instanceCount = 0;
    bool m_dotsGeometryDirty = false;

    unsigned int m_bgVao = 0;
    unsigned int m_bgVbo = 0;
    bool m_lightMode = true;
    bool m_showBg = true;
    QVector3D m_colorLow{0.10f, 0.75f, 1.00f};
    QVector3D m_colorMid{0.10f, 0.95f, 0.35f};
    QVector3D m_colorHigh{1.00f, 0.22f, 0.10f};

    // MVP = Projection * View * Model。
    // 这里 panel/dots 顶点基本已经是“世界坐标”，所以我们用 proj*view 即可（model 先省略）。
    QMatrix4x4 m_mvp;
    QVector3D m_cameraPos{0.0f, 0.0f, 1.0f};

    RenderMode m_renderMode = DataViz;
    LabelMode m_labelMode = LabelsOff;
    int m_hoveredIndex = -1;

    // 早期/预留的矩阵数组（现在主要使用 QMatrix4x4 的 m_mvp，这些暂时保留做对照/扩展）
    float m_proj[16]{};
    float m_view[16]{};
    float m_modelPanel[16]{};

    float m_zoom_ = 45.0;
    float m_camYawDeg = -90.0f;
    float m_camPitchDeg = 0.0f;

    std::atomic<bool> m_rightDown{false};
    QPoint m_lastPos;
    
};


#endif //TACTILEIPC3D_GLWIDGET_H
