#include "task_processer.h"
#include "../include/render/task.h"
#include <iostream>
#include "shared_model.h"
#include "shader.h"
#include "material.h"
#include "scene.h"
#include "graphic.h"


namespace {
[[maybe_unused]] constexpr char DEF[] = "\033[0m";
[[maybe_unused]] constexpr char RED[] = "\033[31m";
[[maybe_unused]] constexpr char GREEN[] = "\033[32m";
[[maybe_unused]] constexpr char YELLOW[] = "\033[33m";
[[maybe_unused]] constexpr char BLUE[] = "\033[34m";
[[maybe_unused]] constexpr char CYAN[] = "\033[36m";

void InvokeCallBack(const Z::Task &task, bool success,
                    const std::array<std::byte, 8> *buf_8bit) {
    if (task.fpCallBack != nullptr) {
        Z::ResultMessage msg{};
        msg.id = task.id;
        msg.res = success ? Z::ResultType::OK : Z::ResultType::FAILED;
        if (buf_8bit)
            std::memcpy(&msg.data, buf_8bit->data(), buf_8bit->size());
        (task.fpCallBack)(msg);
    }
}

void CreateCbufferInfo(Z::ShaderInfo::CbufferInfo *buf, const Shader::CBufferDesc &desc) {
    using namespace Z::ShaderInfo;
    buf->index = static_cast<int>(desc.bindSlot);
    buf->name = desc.name.c_str();
    std::cout << "-- 创建cbuffer,name=" << buf->name << std::endl;
    int c = static_cast<int>(desc.properties.size());
    buf->c_prop = c;
    buf->ar_prop = nullptr;
    // 建构每个prop的描述
    if (c > 0) {
        buf->ar_prop = new PropInfo[c]{};
        for (int i = 0; i < c; ++i) {
            PropInfo &prop = buf->ar_prop[i];
            const Shader::PropertyDesc &dp = desc.properties.at(i);
            prop.index = i;
            prop.name = dp.name.c_str();
            std::cout << "------ 创建prop,name=" << prop.name << std::endl;
            switch (dp.varClass) {
            case D3D_SVC_SCALAR: {
                if (dp.varType == D3D_SVT_INT) {
                    prop.type = PropType::Float;
                } else if (dp.varType == D3D_SVT_FLOAT) {
                    prop.type = PropType::Int;
                }
                break;
            }
            case D3D_SVC_VECTOR: {
                int _dim = static_cast<int>(dp.rows) * static_cast<int>(dp.columns);
                if (_dim == 2) {
                    prop.type = dp.varType == D3D_SVT_FLOAT ? PropType::Float2 : PropType::Int2;
                } else if (_dim == 3) {
                    prop.type = dp.varType == D3D_SVT_FLOAT ? PropType::Float3 : PropType::Int3;
                } else if (_dim == 4) {
                    prop.type = dp.varType == D3D_SVT_FLOAT ? PropType::Float4 : PropType::Int4;
                }
                break;
            }
            case D3D_SVC_MATRIX_COLUMNS:
            case D3D_SVC_MATRIX_ROWS: {
                if (dp.rows == 3) {
                    prop.type = PropType::Matrix3x3;
                } else if (dp.rows == 4) {
                    prop.type = PropType::Matrix4x4;
                }
                break;
            }
            default: {
                MessageBoxW(nullptr, L"未识别的Prop.VarClass",
                            L"TaskProcesser::CreateCbufferInfo",MB_OK);
                break;
            };
            }
            // 拷贝默认值
            constexpr size_t _valuesize = std::min(sizeof(prop.value), sizeof(dp.defaultValue));
            std::memcpy(&prop.value, &dp.defaultValue, _valuesize);
        }
    }
}

}

void ProcessTasks(Graphic *graphic, Scene *scene) {
    Z::Task task{};
    // unsigned char returnData[8]{};
    while (Z::Task::TryGetCommand(task)) {
        bool success{};
        std::array<std::byte, 8> ar_retBuf{};
        switch (task.type) {
        default:
            std::cout << CYAN << "TaskProcesser::未经处理的Task指令" << DEF << std::endl;
            InvokeCallBack(task, false, nullptr);
            break;
        case Z::CmdType::AddRenderObj: {
            std::cout << CYAN << "TaskProcesser::AddRenderObj" << DEF << std::endl;
            Z::Task::AddRenderObj &t = task.cmd.add_render_obj;
            Z::Model *model = t.model;
            //1 添加全局资源 shared model
            SharedModel *sMod = SharedModel::S_AddSharedModel(model, graphic->GetDevice());
            //2 向场景添加一个 renderObj
            //    在Scene内指派Shader和Material
            int id = scene->AddRenderObj(sMod, graphic->GetDevice());
            if (id == -1) { success = false; } else {
                success = true;
                std::memcpy(ar_retBuf.data(), &id, sizeof(int));
            }
            InvokeCallBack(task, success, &ar_retBuf);
            break;
        }
        case Z::CmdType::SetRenderObjMaterial: {
            std::cout << CYAN << "TaskProcesser::SetRenderObjMaterial" << DEF << std::endl;
            Z::Task::SetRenderObjMaterial &t = task.cmd.set_render_obj_material;

            std::string matName{t.matName};
            auto *mat = Material::S_GetMaterial(matName);
            if (!mat) {
                InvokeCallBack(task, false, nullptr);
                break;
            }
            bool ok = scene->SetRenderObjMaterial(t.objIndex, t.meshIndex, mat);
            if (ok) {
                InvokeCallBack(task, true, nullptr);
                break;
            } else {
                InvokeCallBack(task, false, nullptr);
                break;
            }
            break;
        }
        case Z::CmdType::SetMaterialProp: {
            std::cout << CYAN << "TaskProcesser::SetMaterialProp" << DEF << std::endl;
            Z::Task::SetMaterialProp &t = task.cmd.set_material_prop;
            std::string matname{t.matName};

            Material *mat = Material::S_GetMaterial(matname);
            if (!mat) {
                InvokeCallBack(task, false, nullptr);
                break;
            }
            mat->SetPermatCbProp(t.propIndex, t.value);
            InvokeCallBack(task, true, nullptr);
            break;
        }
        case Z::CmdType::AddTextureResource: {
            std::cout << CYAN << "TaskProcesser::AddTextureResource" << DEF << std::endl;
            Z::Task::AddTextureResource &tex = task.cmd.add_texture_resource;
            auto id = Texture2D::S_AddTexture(&tex, graphic->GetDevice());
            if (id >= 0) {
                success = true;
            }
            InvokeCallBack(task, success, &ar_retBuf);
            break;
        }
        case Z::CmdType::CompileShader: {
            using namespace Z::ShaderInfo;
            std::cout << CYAN << "TaskProcesser::CompileShader" << DEF << std::endl;
            Z::Task::CompileShader &t = task.cmd.compile_shader;
            std::u8string u8pathstr{reinterpret_cast<const char8_t *>(t.path)};
            std::filesystem::path path{u8pathstr};
            // 获取,编译shader
            Shader *shader = Shader::S_CompileShader(path, Shader::Entry::VertexShader | Shader::Entry::PixelShader,
                                                     graphic->GetDevice());

            if (!shader) {
                success = false;
                InvokeCallBack(task, success, nullptr);
                break;
            }

            auto *cbdesc = shader->GetCBufferDescs();

            // 将返回给app的desc(Z::ShaderInfo::ShaderInfo)组装好
            ShaderInfo *shaderInfo = new ShaderInfo{};
            shaderInfo->name = shader->m_name.data();

            // 构建cbuffer 结构
            int cc = static_cast<int>(shader->GetCBufferDescs()->size());
            shaderInfo->c_cbuffer = cc;
            if (cc > 0) {
                shaderInfo->ar_cbuffer = new CbufferInfo[cc]{};
                for (int i = 0; i < cc; ++i) {
                    CreateCbufferInfo(&shaderInfo->ar_cbuffer[i], cbdesc->at(i));
                }
            }
            std::cout << "WndRender : cbuffer完了" << std::endl;

            // 构建texture 结构
            const std::vector<Shader::TexBufDesc> *d_t = shader->GetTexBufferDescs();
            int ct = static_cast<int>(d_t->size());
            shaderInfo->c_tex = ct;
            if (ct > 0) {
                shaderInfo->ar_tex = new TexInfo[ct]{};
                for (int i = 0; i < ct; ++i) {
                    shaderInfo->ar_tex[i].name = d_t->at(i).name.c_str();
                    shaderInfo->ar_tex[i].index = i;
                }
            }
            std::cout << "WndRender : ShaderInfo构建完了" << std::endl;

            std::memcpy(ar_retBuf.data(), &shaderInfo, ar_retBuf.size());
            InvokeCallBack(task, true, &ar_retBuf);
            delete shaderInfo;
            break;
        }
        case Z::CmdType::CreateMaterial: {
            //这里默认添加走的是Material::S_Create
            //应该后续继续走PBRMaterial::S_Create的内容(PBR已经添加)
            // 创建material
            std::cout << CYAN << "TaskProcesser::CreateMaterial" << DEF << std::endl;
            Z::Task::CreateMaterial &t = task.cmd.create_material;
            std::string fileName{t.fileName}; // shader的文件名.hlsl
            std::string matName{t.matName};

            Shader *shader = Shader::S_GetShader(fileName);
            if (!shader) {
                InvokeCallBack(task, false, nullptr);
                break;
            }
            Material *mat = Material::S_CreateMaterial(matName, shader, graphic->GetDevice());
            // Material *mat = PBRMaterialFactory::S_Create(matName,shader,graphic->GetDevice());
            if (!mat) {
                InvokeCallBack(task, false, nullptr);
                break;
            }
            InvokeCallBack(task, true, nullptr);
            break;
        }
        case Z::CmdType::SetRenderObjMatrix: {
            std::cout << CYAN << "TaskProcesser::SetRenderObjMatrix" << DEF << std::endl;
            Z::Task::SetRenderObjMatrix &t = task.cmd.set_render_obj_matrix;
            scene->SetRenderObjMatrix(t.id, DirectX::XMFLOAT4X4{t.matrix});
            InvokeCallBack(task, true, nullptr);
            break;
        }
        case Z::CmdType::AssignTextureToMaterial: {
            std::cout << CYAN << "TaskProcesser::AssignTextureToMaterial" << DEF << std::endl;
            Z::Task::AssignTextureToMaterial &t = task.cmd.assign_texture_to_material;

            auto *tex = Texture2D::S_GetTexture2D(t.texName);
            if (!tex) {
                MessageBoxW(nullptr, L"指定的texName未加载", L"WndRender::Task",MB_OK);
                InvokeCallBack(task, false, nullptr);
                break;
            }

            auto *mat = Material::S_GetMaterial(t.matName);
            if (!mat) {
                MessageBoxW(nullptr, L"指定的matName未加载", L"WndRender::Task",MB_OK);
                InvokeCallBack(task, false, nullptr);
                break;
            }

            if (!mat->AssignTexture(tex, t.matTexIdx)) {
                InvokeCallBack(task, false, nullptr);
                break;
            }

            InvokeCallBack(task, true, nullptr);
            break;
        }
        }
    }
}
