#include "wnd_inspector.h"

#include <QLabel>
#include <QStackedLayout>
#include <QScrollArea>
#include <iostream>
#include <QDoubleSpinBox>
#include <QLineEdit>
#include <QMessageBox>
#include "wgt_prop_input.h"
#include <limits>
#include <qevent.h>
#include <QMimeData>

#include "render/task.h"
#include <mutex>

#include "../../render/src/shader.h"

namespace {
std::mutex _g_mtx{};
std::condition_variable _cv{};
Z::ResultMessage g_msg{};
void Callback_Wakeup(Z::ResultMessage msg) {
    g_msg = msg;
    _cv.notify_one();
}
void WaitForResult() {
    std::unique_lock lk{_g_mtx};
    _cv.wait(lk);
}
}

namespace {
WndInspector *_inspector{};
void DisplayItem_(EVENT::InspecItem item) {
    if (_inspector) _inspector->DisplayItem(item);
}
void InitSpinBox(QDoubleSpinBox *box) {
    box->setMaximumWidth(80);
    box->setMinimum(std::numeric_limits<float>::lowest());
    box->setMaximum(std::numeric_limits<float>::max());
    box->setButtonSymbols(QAbstractSpinBox::NoButtons);
}

class Row : public QWidget {
public:
    explicit Row(float *v3, float step = 0.5f, void (*callback)(void *) = nullptr, void *parameter = nullptr) :
    values{v3, v3 + 1, v3 + 2}, temp_value{*v3, *(v3 + 1), *(v3 + 2)},
    para(parameter), fpCallback(callback) {
        this->setLayout(new QHBoxLayout);
        this->layout()->setSpacing(10);

        mx = new QDoubleSpinBox{};
        InitSpinBox(mx);
        mx->setValue(*values[0]);
        mx->setSingleStep(step);
        connect(mx, &QDoubleSpinBox::valueChanged, this, &Row::OnValueChangedX);

        my = new QDoubleSpinBox{};
        InitSpinBox(my);
        my->setValue(*values[1]);
        my->setSingleStep(step);
        connect(my, &QDoubleSpinBox::valueChanged, this, &Row::OnValueChangedY);

        mz = new QDoubleSpinBox{};
        InitSpinBox(mz);
        mz->setValue(*values[2]);
        mz->setSingleStep(step);
        connect(mz, &QDoubleSpinBox::valueChanged, this, &Row::OnValueChangedZ);

        this->layout()->addWidget(mx);
        this->layout()->addWidget(my);
        this->layout()->addWidget(mz);
    };

    float *GetFloat3() {
        return temp_value;
    }

private:
    void OnValueChangedX(double v) {
        temp_value[0] = static_cast<float>(v);
        if (fpCallback) {
            fpCallback(para);
        }
    }
    void OnValueChangedY(double v) {
        temp_value[1] = static_cast<float>(v);
        if (fpCallback) {
            fpCallback(para);
        }
    }
    void OnValueChangedZ(double v) {
        temp_value[2] = static_cast<float>(v);
        if (fpCallback) {
            fpCallback(para);
        }
    }

    float *values[3];
    float temp_value[3];

    QDoubleSpinBox *mx;
    QDoubleSpinBox *my;
    QDoubleSpinBox *mz;

    void *para;
    void (*fpCallback)(void *para);
};

//region RenderObject 展示面板
class WgtMaterial : public QWidget {
public:
    explicit WgtMaterial(MAT::MatInstance *mat, RENDEROBJ::Object *obj, int modelIdx, int meshIdx) :
    m_renderObj(obj), m_modelIdx(modelIdx), m_meshIdx(meshIdx), m_mat(mat) {
        // 这里获取的mat可能是nullptr,此时用的是default material
        setAcceptDrops(true);
        setLayout(new QVBoxLayout);
        setStyleSheet("background-color:#445522;");
        resize(100, 30);
        QString disp = mat ? QString{mat->name.c_str()} : QString{"Default"};
        m_label = new QLabel{disp};
        layout()->addWidget(m_label);
    }

public:
    RENDEROBJ::Object *m_renderObj{};
    int m_modelIdx;
    int m_meshIdx;
    MAT::MatInstance *m_mat;
    QLabel *m_label{};

protected:
    void dragEnterEvent(QDragEnterEvent *event) override {
        const QMimeData *mime = event->mimeData();
        if (mime->hasFormat("application/x-material")) {
            event->acceptProposedAction();
            event->accept();
        } else {
            event->ignore();
        }
    }
    void dropEvent(QDropEvent *event) override {
        QByteArray data = event->mimeData()->data("application/x-material");
        std::string matNameStr{data.data()};
        std::cout << "接收到str:" << matNameStr << std::endl; // 这里的data_str是material的名字
        // 通知 renderer 设置材质
        Z::Task task{};
        task.type = Z::CmdType::SetRenderObjMaterial;
        task.fpCallBack = Callback_Wakeup;
        task.cmd.set_render_obj_material.matName = matNameStr.c_str();
        task.cmd.set_render_obj_material.meshIndex = m_meshIdx;
        task.cmd.set_render_obj_material.objIndex = m_modelIdx;
        Z::Task::AddCommand(task);
        WaitForResult();
        if (g_msg.res != Z::ResultType::OK) {
            QMessageBox::about(nullptr, "错误", "设置material失败");
            return;
        }
        // 改app端renderObj的material表
        MAT::MatInstance *_mat = LoadedResource::GetLoadedMaterial(matNameStr);
        m_renderObj->m_mats[m_meshIdx] = _mat;
        m_mat = _mat;
        // 改ui
        m_label->setText(QString::fromUtf8(matNameStr.c_str()));
    }


};

void RenderObjPanelCallback(void *panel);
class RenderObjPanel : public QWidget {
public:
    explicit RenderObjPanel(RENDEROBJ::Object *obj) : obj(obj) {
        setLayout(new QVBoxLayout);
        layout()->setSpacing(0);
        layout()->setAlignment(Qt::AlignTop);

        layout()->addWidget(new QLabel("position"));
        rowPos = new Row(obj->position, 0.5f, RenderObjPanelCallback, this);
        layout()->addWidget(rowPos);

        layout()->addWidget(new QLabel("rotation"));
        rowRot = new Row(obj->rotation, 15.0f, RenderObjPanelCallback, this);
        layout()->addWidget(rowRot);

        layout()->addWidget(new QLabel("scale"));
        rowScale = new Row(obj->scale, 0.1f, RenderObjPanelCallback, this);
        layout()->addWidget(rowScale);

        layout()->addWidget(new QLabel("Materials"));
        for (int i = 0; i < obj->m_mats.size(); ++i) {
            // 这里的m_mats[i]可能是nullptr(用的default)
            layout()->addWidget(new WgtMaterial{obj->m_mats[i], obj, obj->id, i});
        }
    }

    void UpdateValues() const {
        bool ret = obj->Update(rowPos->GetFloat3(), rowRot->GetFloat3(), rowScale->GetFloat3());
        (void)ret;
    }

public:
    RENDEROBJ::Object *obj;
    Row *rowPos;
    Row *rowRot;
    Row *rowScale;
};
void RenderObjPanelCallback(void *panel) {
    static_cast<RenderObjPanel *>(panel)->UpdateValues();
}
//endregion


//region Material 展示面板
class WgtTexBox : public QWidget {
public:
    explicit WgtTexBox(MAT::MatInstance *mat, int matTexIdx) :
    m_mat(mat), m_MatTexIdx(matTexIdx) {
        setAcceptDrops(true);
        setAttribute(Qt::WidgetAttribute::WA_StyledBackground, true);
        setStyleSheet("background-color:#666666;");
        m_texInfo = &mat->info.textures[matTexIdx];

        QVBoxLayout *lay = new QVBoxLayout{this};
        lay->setSpacing(0);
        lay->setContentsMargins(0, 0, 0, 0);
        setLayout(lay);

        QString texName{m_texInfo->cur_tex.empty() ? "" : m_texInfo->cur_tex.c_str()};

        m_label = new QLabel{texName};
        lay->addWidget(m_label);
    }

protected:
    void dragEnterEvent(QDragEnterEvent *event) override {
        if (event->mimeData()->hasFormat("application/x-texture")) {
            event->acceptProposedAction();
            event->accept();
        } else {
            event->ignore();
        }
    }
    void dropEvent(QDropEvent *event) override {
        auto *mime = event->mimeData();
        auto data = mime->data("application/x-texture");
        // 获取的data是Res::path转换为UTF8的str
        std::u8string u8pathstr{reinterpret_cast<const char8_t *>(data.data()),
            static_cast<unsigned long long>(data.size())};
        // std::string pathstr{data.data(), static_cast<unsigned long long>(data.size())};
        // std::cout << "获取dropevent,data=" << pathstr << std::endl;

        std::filesystem::path path{u8pathstr};
        std::u8string u8texName = path.stem().u8string();

        // 向Renderer发起请求,设置texture给material
        Z::Task task{};
        task.type = Z::CmdType::AssignTextureToMaterial;
        task.fpCallBack = Callback_Wakeup;
        task.cmd.assign_texture_to_material.texName = reinterpret_cast<const char *>(u8texName.c_str());
        task.cmd.assign_texture_to_material.matName = m_mat->name.c_str();
        task.cmd.assign_texture_to_material.matTexIdx = m_MatTexIdx;
        Z::Task::AddCommand(task);
        WaitForResult();
        if (g_msg.res != Z::ResultType::OK) {
            return;
        }

        // 更新UI和APP端数据
        m_mat->info.textures[m_MatTexIdx].cur_tex.assign(
            reinterpret_cast<const char *>(u8texName.c_str()), u8pathstr.size());
        m_label->setText(QString{u8texName.c_str()});

    }

public:
    QLabel *m_label{};

    MAT::MatInstance *m_mat;
    int m_MatTexIdx;
    const SHADER::TextureInfo *m_texInfo{};

    void (*fpOnValueChange)(MAT::MatInstance *, int, SHADER::TextureInfo *){nullptr};
}; //class WgtTexBox

class WgtTexture : public QWidget {
public:
    explicit WgtTexture(MAT::MatInstance *mat, int matTexIdx) : m_mat(mat), m_matTexIdx(matTexIdx) {
        this->setLayout(new QHBoxLayout);

        auto *tex = &mat->info.textures[matTexIdx];

        QLabel *lab = new QLabel(tex->name.c_str());
        lab->setMaximumWidth(80);
        WgtTexBox *box = new WgtTexBox(mat, matTexIdx);
        layout()->addWidget(lab);
        layout()->addWidget(box);
    }

public:
    MAT::MatInstance *m_mat;

    int m_matTexIdx;

}; //class WgtTexture

void UpdateMaterial(void *pmat, int index) {
    MAT::MatInstance *mat = static_cast<MAT::MatInstance *>(pmat);
    mat->Update(index);
}

class CBufferPanel : public QWidget {
public:
    explicit CBufferPanel(SHADER::CbufferInfo *cb, MAT::MatInstance *mat) : m_cbInfo(cb) {
        QVBoxLayout *lay = new QVBoxLayout{};
        lay->setContentsMargins(0, 5, 0, 5);
        setLayout(lay);
        lay->addWidget(new QLabel{QString{"CBuffer Name: "} + QString{cb->name.c_str()}
                                  + QString{" OdrInArr: "} + std::to_string(cb->index).c_str()});

        for (auto &prop: cb->props) {
            lay->addWidget(new QLabel{std::to_string(prop.index).c_str() + QString{" : "} +
                                      QString{prop.name.c_str()}});
            WgtPropInput *panelProp = new WgtPropInput{prop, UpdateMaterial, mat, prop.index};
            lay->addWidget(panelProp);
        }
    }

    SHADER::CbufferInfo *m_cbInfo;
}; // class CBufferPanel

class MaterialPanel : public QWidget {
public:
    explicit MaterialPanel(MAT::MatInstance *mat) : m_material(mat) {
        this->setContentsMargins(0, 0, 0, 0);
        setLayout(new QVBoxLayout);
        layout()->setAlignment(Qt::AlignTop);
        layout()->addWidget(new QLabel("Shader: " + QString{mat->info.name.c_str()}));
        layout()->addWidget(new QLabel("Material: " + QString{mat->name.c_str()}));

        // 添加 CBUFFER PerMat 的展示面板
        for (SHADER::CbufferInfo &cb: mat->info.cbuffer) {
            if (cb.name == "CBPerMat") {
                CBufferPanel *cbpanel = new CBufferPanel{&cb, mat};
                layout()->addWidget(cbpanel);
            }
        }

        // 添加贴图框框
        for (SHADER::TextureInfo &texb: mat->info.textures) {
            std::cout << "texture的名字:" << texb.name << std::endl;
            WgtTexture *texwgt = new WgtTexture(mat, texb.index);
            layout()->addWidget(texwgt);
        }
    }

    MAT::MatInstance *m_material{};
};

//endregion


//region Shader 展示面板

std::string PropTypeToStrhelper(Z::ShaderInfo::PropType type) {
    switch (type) {
    case Z::ShaderInfo::PropType::Matrix3x3: return {"Matrix3x3"};
    case Z::ShaderInfo::PropType::Matrix4x4: return {"Matrix4x4"};
    case Z::ShaderInfo::PropType::Float: return {"Float"};
    case Z::ShaderInfo::PropType::Float2: return {"Float2"};
    case Z::ShaderInfo::PropType::Float3: return {"Float3"};
    case Z::ShaderInfo::PropType::Float4: return {"Float4"};
    case Z::ShaderInfo::PropType::Int: return {"Int"};
    case Z::ShaderInfo::PropType::Int2: return {"Int2"};
    case Z::ShaderInfo::PropType::Int3: return {"Int3"};
    case Z::ShaderInfo::PropType::Int4: return {"Int4"};
    default: return {"None"};
    }
}
class DisplayLR : public QWidget {
public:
    explicit DisplayLR(const std::string &left, const std::string &right, int leftMaxPx = 120) {
        QHBoxLayout *lay = new QHBoxLayout{this};
        lay->setContentsMargins(10, 0, 10, 0);
        this->setLayout(lay);
        QLabel *l = new QLabel{QString::fromStdString(left)};
        l->setMaximumWidth(leftMaxPx);
        lay->addWidget(l);
        lay->addWidget(new QLabel{QString::fromStdString(right)});
    }

public:

}; // class DisplayLR

class ShaderPanel : public QWidget {
public:
    explicit ShaderPanel(const SHADER::ShaderInfo *shader) : m_shader(shader) {
        // 读取shader desc，显示在面板上
        QVBoxLayout *lay = new QVBoxLayout{this};
        this->setLayout(lay);
        lay->setContentsMargins(0, 0, 0, 0);
        lay->setSpacing(0);
        lay->setAlignment(Qt::AlignTop);

        // name
        lay->addWidget(new QLabel{QString{"ShaderName:"} + QString::fromStdString(shader->name)});

        // cbuffers
        lay->addSpacing(10);
        for (const SHADER::CbufferInfo &item: shader->cbuffer) {
            lay->addWidget(new QLabel{QString{"CBuffer Name:"} + QString::fromStdString(item.name)});
            for (const SHADER::PropertyInfo &prop: item.props) {
                auto *wgt = new DisplayLR(prop.name, PropTypeToStrhelper(prop.type), 150);
                lay->addWidget(wgt);
            }
        }

        // texture buffer
        lay->addSpacing(10);
        if (shader->textures.size() > 0) {
            lay->addWidget(new QLabel{QString{"Textures"}});
        }
        for (const SHADER::TextureInfo &item: shader->textures) {
            lay->addWidget(new QLabel{QString::fromStdString(item.name)});
        }

    }

public:
    const SHADER::ShaderInfo *const m_shader;

}; // class ShaderPanel

//endregion Shader 展示面板


}

//-blue class WndInspector
//= public
WndInspector::WndInspector(QWidget *parent) : QWidget(parent) {
    setAttribute(Qt::WA_StyledBackground, true);
    _inspector = this;
    m_Connection = std::move(EVENT::OnSelectItem.Subscribe(&DisplayItem_));
    this->setLayout(new QVBoxLayout);
    m_scroll = new QScrollArea;
    m_scroll->setLayout(new QVBoxLayout);
    this->layout()->addWidget(m_scroll);
    m_scroll->layout()->setAlignment(Qt::AlignTop);
}
void WndInspector::DisplayItem(EVENT::InspecItem &item) {
    std::cout << "WndInspector::DisplayItem Item.Type = " << item.type << std::endl;
    m_Item = item;

    // 删除旧面板
    if (m_content) {
        m_content->deleteLater();
        m_content = nullptr;
    }

    // 创建新面板 赋值给m_content
    switch (item.type) {
    case SelectableItem::RenderObj: {
        RENDEROBJ::Object *obj{};
        std::memcpy(&obj, &item.resource, 8);
        m_content = new RenderObjPanel{obj};

        break;
    }
    case SelectableItem::Model: {
        break;
    }
    case SelectableItem::Material: {
        MAT::MatInstance *mat{};
        std::memcpy(&mat, &item.resource, 8);
        m_content = new MaterialPanel{mat};
        break;
    }
    case SelectableItem::Texture: {
        break;
    }
    case SelectableItem::Shader: {
        SHADER::ShaderInfo *shader{};
        std::memcpy(&shader, &item.resource, 8);
        m_content = new ShaderPanel{shader};
        break;
    }
    default: { break; }
    }

    // 将新的panel添加进显示区域
    if (!m_content) {
        // QMessageBox::about(this, "WndInspector::DisplayItem", "未初始化m_content");
        return;
    }
    m_scroll->layout()->addWidget(m_content);
    m_scroll->setWidget(m_content);
    m_scroll->setWidgetResizable(true);
}



//= private



















//
