#include "wnd_resource.h"
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QButtonGroup>
#include <QScrollArea>
#include <QLabel>
#include <filesystem>
#include <QFileDialog>

#include "wgt_listitem.h"
#include "resource.h"
#include <zassimp/importer.h>

#include "states.h"
#include <QMessageBox>
#include "render/task.h"
#include <QStandardPaths>
#include <QPointer>
#include <mutex>
#include "wnd_create_mat.h"
#include <iostream>
#include "wnd_add_tex.h"

namespace {
// callback wait
std::mutex mtx_cb{};
std::condition_variable cv_cb{};
Z::ResultMessage g_CallbackMessage{};
void CallBack_WakeThreadUp(Z::ResultMessage msg) { // 被Renderer回调
    g_CallbackMessage = msg;
    cv_cb.notify_all();
}
void WaitForResult() {
    std::unique_lock lk(mtx_cb);
    cv_cb.wait(lk);
}

// open file window
QString lastFiledir;
std::filesystem::path OpenQFileSelector(const QString &caption,
                                        const QString *suffixs = nullptr) {
    QString ret;
    QString dir = lastFiledir.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) : lastFiledir;
    if (suffixs) {
        ret = QFileDialog::getOpenFileName(nullptr, caption, dir, *suffixs);
    } else {
        ret = QFileDialog::getOpenFileName(nullptr, caption, dir);
    }

    std::filesystem::path path{ret.toStdU16String()};
    lastFiledir = ret;
    return path;
}

SelectableItem EventType(WndResource::Mode mode) {
    switch (mode) {
    case WndResource::Mode::Model:
        return SelectableItem::Model;

    case WndResource::Mode::Material:
        return SelectableItem::Material;

    case WndResource::Mode::Texture:
        return SelectableItem::Texture;

    case WndResource::Mode::Shader:
        return SelectableItem::Shader;

    default: return SelectableItem::None;
    }
}
}


//-purple
//= public
WndResource::WndResource(QWidget *parent) : QWidget(parent) {
    setAttribute(Qt::WA_StyledBackground, true);
    setLayout(new QVBoxLayout(this));
    layout()->setSpacing(0);
    setMinimumWidth(100);


    // btn list
    QWidget *btnGrp = new QWidget(this);
    btnGrp->setFixedHeight(40);
    btnGrp->setLayout(new QHBoxLayout(btnGrp));
    btnGrp->layout()->setContentsMargins(0, 0, 0, 0);
    btnGrp->layout()->setSpacing(0);

    QPushButton *btn_model = new QPushButton("Model", btnGrp);
    btn_model->setCheckable(true);
    connect(btn_model, &QPushButton::clicked,
            this, [this]() { SelectMode(Mode::Model); });
    QPushButton *btn_material = new QPushButton("Material", btnGrp);
    btn_material->setCheckable(true);
    connect(btn_material, &QPushButton::clicked,
            this, [this]() { SelectMode(Mode::Material); });
    QPushButton *btn_texture = new QPushButton("Texture", btnGrp);
    btn_texture->setCheckable(true);
    connect(btn_texture, &QPushButton::clicked,
            this, [this]() { SelectMode(Mode::Texture); });
    QPushButton *btn_shader = new QPushButton("Shader", btnGrp);
    btn_shader->setCheckable(true);
    connect(btn_shader, &QPushButton::clicked,
            this, [this]() { SelectMode(Mode::Shader); });

    QButtonGroup *grp = new QButtonGroup(this);
    grp->addButton(btn_model);
    grp->addButton(btn_material);
    grp->addButton(btn_texture);
    grp->addButton(btn_shader);

    btnGrp->layout()->addWidget(btn_model);
    btnGrp->layout()->addWidget(btn_material);
    btnGrp->layout()->addWidget(btn_texture);
    btnGrp->layout()->addWidget(btn_shader);


    // statcked widget
    m_stack = new QStackedWidget(this);

    // load button
    QPushButton *btn_load = new QPushButton("Load Resource", this);
    btn_load->setFixedHeight(45);
    connect(btn_load, &QPushButton::clicked, this, [this]() {
        OnBtnLoadResource();
    });

    layout()->addWidget(btnGrp);
    layout()->addWidget(m_stack);
    dynamic_cast<QVBoxLayout *>(layout())->addSpacing(8);
    layout()->addWidget(btn_load);
    btn_model->click();
}

//= private
void WndResource::SelectMode(Mode mode) {
    if (m_Mode == mode) return;
    m_Mode = mode;
    switch (mode) {
    case Mode::Model: {
        ShowModel();
        break;
    }
    case Mode::Material: {
        ShowMat();
        break;
    }
    case Mode::Texture: {
        ShowTex();
        break;
    }
    case Mode::Shader: {
        ShowShader();
        break;
    }
    default: break;
    }
}
void WndResource::ShowModel() {
    UnselectAllItem();
    if (m_model) {
        m_stack->removeWidget(m_model);
        m_model->deleteLater();
    }
    m_model = new QScrollArea{this};
    InitPanel(m_model, m_lstModel, "application/x-model");
    m_stack->addWidget(m_model);
    m_stack->setCurrentWidget(m_model);
}
void WndResource::ShowMat() {
    UnselectAllItem();
    if (m_material) {
        m_stack->removeWidget(m_material);
        m_material->deleteLater();
    }
    m_material = new QScrollArea{this};
    InitPanel(m_material, m_lstMaterial, "application/x-material");
    m_stack->addWidget(m_material);
    m_stack->setCurrentWidget(m_material);
}
void WndResource::ShowTex() {
    UnselectAllItem();
    if (m_texture) {
        m_stack->removeWidget(m_texture);
        m_texture->deleteLater();
    }
    m_texture = new QScrollArea{this};
    InitPanel(m_texture, m_lstTexture, "application/x-texture");
    m_stack->addWidget(m_texture);
    m_stack->setCurrentWidget(m_texture);
}
void WndResource::ShowShader() {
    UnselectAllItem();
    if (m_shader) {
        m_stack->removeWidget(m_shader);
        m_shader->deleteLater();
    }
    m_shader = new QScrollArea{this};
    InitPanel(m_shader, m_lstShader, "application/x-shader");
    m_stack->addWidget(m_shader);
    m_stack->setCurrentWidget(m_shader);
}

void WndResource::UnselectAllItem() {
    if (m_selectedItem) m_selectedItem->SetSelected(false);
    m_selectedItem = nullptr;
}

void WndResource::OnBtnLoadResource() {
    switch (m_Mode) {
    case Mode::Model: {
        QString sfx = "*.fbx";
        auto path = OpenQFileSelector("Open FBX File", &sfx);
        if (path.empty()) return;
        //开始导入模型
        Z::Model *model{};
        if (!ModelImporter::LoadFromFile(path, &model)) return;
        //导入成功,加入资源,渲染
        std::u8string u8path{};
        u8path.reserve(model->name.size());
        for (unsigned char ch: model->name) {
            u8path.push_back(static_cast<char8_t>(ch));
        }
        m_lstModel.push_back({u8path});
        ShowModel();

        break;
    }
    case Mode::Material: {
        std::string res_shader{};
        std::string res_matName{};
        std::vector<std::string> inputs{};
        inputs.reserve(m_lstShader.size());
        for (auto &item: m_lstShader) {
            std::u8string u8name = item.path.filename().u8string();
            inputs.emplace_back(reinterpret_cast<const char *>(u8name.c_str()), u8name.size());
        }
        WndCreateMat dia{this, inputs, res_shader, res_matName};
        dia.exec();

        if (res_matName.empty() || res_shader.empty()) {
            break;
        }
        if (res_matName == "Default") {
            QMessageBox::about(this, "错误", "Material Name can't be \"Default\"");
            break;
        }
        std::cout << "\033[33m" << "WndResource::OnBtnLoadResource::创建Material\n" <<
                "输入的matname=" << res_matName << " 选中的shadername=" << res_shader << "\033[0m" << std::endl;
        SHADER::ShaderInfo *shader = LoadedResource::GetLoadedShader(res_shader);
        if (!shader) {
            QMessageBox::about(this, "错误", "Shader未载入");
            break;
        }
        if (LoadedResource::GetLoadedMaterial(res_matName)) {
            QMessageBox::about(this, "错误", "Material Name重复\n未添加");
            break;
        }

        // 让 RENDERER 创建材质
        Z::Task task{};
        task.type = Z::CmdType::CreateMaterial;
        task.cmd.create_material.fileName = res_shader.c_str();
        task.cmd.create_material.matName = res_matName.c_str();
        task.fpCallBack = CallBack_WakeThreadUp;

        Z::Task::AddCommand(task);
        WaitForResult();
        if (g_CallbackMessage.res != Z::ResultType::OK) {
            QMessageBox::about(this, "错误", "创建material失败");
            break;
        }
        // APP UI 渲染MAT
        MAT::MatInstance *_mat = MatCreater::CreateMat(res_matName, shader);
        std::u8string u8matname{reinterpret_cast<const char8_t *>(res_matName.c_str())};
        m_lstMaterial.push_back({u8matname, _mat});
        ShowMat();

        break;
    }
    case Mode::Texture: {
        QString suffix{"*.jpg *.png"};
        auto path = OpenQFileSelector("Select Image File", &suffix);
        if (path.empty()) break;

        if (LoadedResource::GetLoadedTexture(path) != nullptr) {
            QMessageBox::about(this, "WndResource", "已经加载此贴图\n");
            break;
        }

        // 根据Ret的配置指派图像加载路径
        WndAddTex::ReturnData ret{};
        WndAddTex waddt{ret};
        waddt.exec();
        std::cout << "Ret.srgb = " << std::boolalpha << ret.SRGB <<
                "\nRet.ReverseY = " << std::boolalpha << ret.reverse_Y <<
                "\nRet.bpp16 = " << std::boolalpha << ret.bpp_16 <<
                "\nRet.Type = " << static_cast<int>(ret.textureType) <<
                std::endl;
        if (!ret.valid) { break; }

        Z::TextureData *tex = ImgImporter::ImportFromFile(path, ret.reverse_Y, ret.textureType);
        if (tex == nullptr) {
            QMessageBox::about(this, "WndResource", "加载本地图片失败");
            break;
        }

        // 给GPU端发送信息,初始化该TEXTURE的资源
        Z::Task t{};
        t.type = Z::CmdType::AddTextureResource;
        t.cmd.add_texture_resource.texName = tex->name.c_str();
        t.cmd.add_texture_resource.type = tex->type;
        t.cmd.add_texture_resource.dataptr = tex->data.data();
        t.cmd.add_texture_resource.bytesPerPixel = tex->bytesPerPixel;
        t.cmd.add_texture_resource.rowPitch = tex->rowPitch;
        t.cmd.add_texture_resource.size = tex->size;
        t.cmd.add_texture_resource.bpp16 = ret.bpp_16;
        t.cmd.add_texture_resource.SRGB = ret.SRGB;
        t.fpCallBack = CallBack_WakeThreadUp;
        Z::Task::AddCommand(t);
        WaitForResult();
        if (g_CallbackMessage.res != Z::ResultType::OK) {
            delete tex;
            QMessageBox::about(this, "WndResource", "创建 GPU Tex 资源失败");
            break;
        }
        // 渲染UI
        m_lstTexture.emplace_back(path, nullptr);
        ShowTex();

        break;
    }
    case Mode::Shader: {
        QString suffix{"*.hlsl"};
        auto path = OpenQFileSelector("Select .hlsl Shader File", &suffix);
        if (path.empty()) return;

        std::filesystem::path fileName = path.filename();
        std::u8string u8name = fileName.u8string();
        std::string name{reinterpret_cast<const char *>(u8name.c_str()), u8name.size()};

        if (LoadedResource::GetLoadedShader(name)) {
            QMessageBox::about(this, "WndResource OnBtnLoadResource", "重复的shader名");
            break;
        }
        SHADER::ShaderInfo *shaderInfo = ShaderImporter::ImportFromFile(path);
        if (shaderInfo) {
            m_lstShader.push_back({fileName, shaderInfo});
            ShowShader();
        }
        break;
    }
    default: break;
    }
}
void WndResource::InitPanel(QScrollArea *scroll, const std::list<Res> &list, const char *type) {
    QWidget *content = new QWidget{scroll};
    QVBoxLayout *layout = new QVBoxLayout{content};
    content->layout()->setContentsMargins(0, 0, 0, 0);
    content->layout()->setSpacing(0);

    auto *itemGroup = SelectedItemWidgetGroup::GetGroup("inspector");
    // 根据具体资源list渲染列表
    for (auto &item: list) {
        auto *label = new QLabel{QString::fromStdWString(item.path.stem().wstring())};
        auto *itm = new DragItemWidget{label, content, itemGroup};

        std::u8string u8mime{item.path.u8string()};
        std::string mime = {reinterpret_cast<const char *>(u8mime.c_str()), u8mime.size()};
        itm->SetMime(type, mime.c_str());

        connect(itm, &ListItemWidget::Clicked, itm, [this,&item](ListItemWidget *i) {
            if (this->m_selectedItem != nullptr) this->m_selectedItem->SetSelected(false);
            this->m_selectedItem = i;
            i->SetSelected(true);
            EVENT::InspecItem ev{};
            ev.type = EventType(m_Mode);
            ev.path = std::string{reinterpret_cast<const char *>(item.path.u8string().c_str())};
            ev.resource = item.data;
            EVENT::OnSelectItem.EmitEvent(std::move(ev));

        });

        content->layout()->addWidget(itm);
    }

    layout->addStretch();

    scroll->setWidget(content);
    scroll->setWidgetResizable(true);

}




















//
