#include "render_object.h"
#include <vector>
#include <iostream>
#include <DirectXMath.h>

#include "material.h"
#include "shared_model.h"
#include <tool.h>


namespace {

}

//MARK: RenderMesh
//= RenderMesh public

void RenderMesh::Update_CBPerObj(ID3D11DeviceContext *ctx) {
    using namespace DirectX;
    if (b_baseObj_dirty) {
        // 更新自己的PerObj CB(world)
        XMMATRIX w = XMMatrixMultiply(XMLoadFloat4x4(&m_localMatrix),
                                      XMLoadFloat4x4(&m_baseRenderObj->m_worldMatrix));
        //// 如果用 column vector 风格,则置换乘法方向
        // XMMATRIX w = XMMatrixMultiply(XMLoadFloat4x4(&m_baseRenderObj->m_worldMatrix),
        //                               XMLoadFloat4x4(&m_localMatrix));

        XMStoreFloat4x4(&m_cb_state.world, w);
        //// 如果使用column vector风格,则需要转置
        // XMStoreFloat4x4(&m_cb_state.world, XMMatrixTranspose(w));

        //// [pbr-add] row-vector 法线变换：N' = N * transpose(inverse(World))
        const XMVECTOR determinant = XMMatrixDeterminant(w);
        if (std::abs(XMVectorGetX(determinant)) > 1.0e-8f) {
            const XMMATRIX worldInvTranspose = XMMatrixTranspose(XMMatrixInverse(nullptr, w));
            XMStoreFloat4x4(&m_cb_state.worldInvTranspose, worldInvTranspose);
        } else {
            // 奇异矩阵没有合法法线矩阵；用 identity 避免 NaN 扩散。
            XMStoreFloat4x4(&m_cb_state.worldInvTranspose, XMMatrixIdentity());
        }

        // 上传自己的PerObj CB(world)，并设置为当前的2号buffer(per obj buffer)
        D3D11_MAPPED_SUBRESOURCE mapped{};
        HRESULT hr = ctx->Map(m_cbuffer.Get(), 0,
                              D3D11_MAP_WRITE_DISCARD, 0, &mapped);
        if (SUCCEEDED(hr)) {
            std::memcpy(mapped.pData, &m_cb_state, sizeof(CBPerObj));
            ctx->Unmap(m_cbuffer.Get(), 0);
            b_baseObj_dirty = false;
        }
    }

    // ctx->VSSetConstantBuffers(2, 1, m_cbuffer.GetAddressOf());
    ctx->VSSetConstantBuffers(Z::RenderBindings::PerObjectCB, 1, m_cbuffer.GetAddressOf());
}
// main pass
void RenderMesh::Update_BindMat(ID3D11DeviceContext *ctx) {
    //TODO 暂时在mesh这里更新MATERIAL的CBUFFER
    //     未来作SRP BATCHER

    //设定b1为material的CBPerMat
    m_material->Bind(ctx);

}
void RenderMesh::Draw_Main(ID3D11DeviceContext *ctx) const {
    auto *shader = m_material->GetShader();
    //设置shader,inputlayout等
    ctx->IASetInputLayout(shader->m_input_layout.Get()); // todo 让renderer/pass设置
    ctx->VSSetShader(shader->m_vertex_shader.Get(), nullptr, 0); // todo 让mat设置
    ctx->PSSetShader(shader->m_pixel_shader.Get(), nullptr, 0); // todo 让mat设置
    //设置每个 mesh 的 vertex buffer / index buffer
    UINT stride = sizeof(Z::VertexData);
    UINT offset = 0;
    ctx->IASetVertexBuffers(0, 1, m_sharedMesh->buf_vert.GetAddressOf(),
                            &stride, &offset);
    ctx->IASetIndexBuffer(m_sharedMesh->buf_index.Get(), DXGI_FORMAT_R32_UINT, 0);
    ctx->DrawIndexed(m_sharedMesh->countIndices, 0, 0);
}
// shadow pass
void RenderMesh::Draw_Shadow(ID3D11DeviceContext *ctx) const {
    //设置每个 mesh 的 vertex buffer / index buffer
    UINT stride = sizeof(Z::VertexData);
    UINT offset = 0;
    ctx->IASetVertexBuffers(0, 1, m_sharedMesh->buf_vert.GetAddressOf(),
                            &stride, &offset);
    ctx->IASetIndexBuffer(m_sharedMesh->buf_index.Get(), DXGI_FORMAT_R32_UINT, 0);
    ctx->DrawIndexed(m_sharedMesh->countIndices, 0, 0);
}

//= RenderMesh Private
RenderMesh::RenderMesh(const RenderObject *obj, UINT idx, Material *dftMat, ID3D11Device *device) :
m_modelIdx(idx), m_baseRenderObj(obj), m_localMatrix(obj->m_model->ar_sharedMesh[idx].localTsf),
m_sharedMesh(&obj->m_model->ar_sharedMesh[idx]), m_material(dftMat) {
    if (!InitGpuResources(device)) {
        throw std::runtime_error("Init Render Mesh GPU Resources ERROR");
    }
}


bool RenderMesh::InitGpuResources(ID3D11Device *device) {
    D3D11_BUFFER_DESC d_cb{};
    d_cb.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    d_cb.Usage = D3D11_USAGE_DYNAMIC;
    d_cb.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    d_cb.ByteWidth = (sizeof(CBPerObj) + 15u) & ~15u;
    HRESULT hr = device->CreateBuffer(&d_cb, nullptr, m_cbuffer.GetAddressOf());
    if (FAILED(hr)) {
        MessageBoxW(nullptr, L"Create PerObj CB failed",
                    L"RenderMesh::InitGPUResources",MB_OK);
        return false;
    }
    return true;
}
void RenderMesh::OnBaseObjMatrixChanged() {
    b_baseObj_dirty = true;
}


//MARK: RenderObject
//= Render Object static
RenderObject *RenderObject::S_CreateRenderObj(SharedModel *model, Material *dftMat,
                                              ID3D11Device *device) {
    try {
        RenderObject *obj = new RenderObject(model, dftMat, device);
        return obj;
    } catch (std::exception &e) {
        MessageBoxW(nullptr, Z::u8_to_wstring(e.what()).c_str(),
                    L"CreateRenderObj",MB_OK);
        return nullptr;
    }
}


//= public
bool RenderObject::SetMeshMaterial(int meshId, Material *mat) {
    if (0 <= meshId && meshId < m_renderMeshes.size()) {
        m_renderMeshes[meshId].SetMaterial(mat);
        return true;
    } else {
        MessageBoxW(nullptr, L"RenderObject", L"m_renderMeshes越界",MB_OK);
        return false;
    }
}


//= private
RenderObject::RenderObject(SharedModel *model, Material *dftMat, ID3D11Device *device) {
    m_model = model;
    DirectX::XMStoreFloat4x4(&m_worldMatrix, DirectX::XMMatrixIdentity());
    m_renderMeshes.reserve(model->ar_sharedMesh.size());
    for (UINT i = 0; i < model->ar_sharedMesh.size(); ++i) {
        m_renderMeshes.push_back({this, i, dftMat, device});
    }
}
RenderObject::~RenderObject() = default;














//
