// ReSharper disable CppUseStructuredBinding
#include "scene.h"
#include "shader.h"
#include <iostream>
#include "material.h"


//= public
Scene::Scene(ID3D11Device &device) {
    // 统一 lightDir 为“光线传播方向”
    DirectX::XMStoreFloat3(&m_state.lightDir, DirectX::XMVector3Normalize(
                               DirectX::XMLoadFloat3(&m_state.lightDir)));
    if (!InitGpuResources(&device)) {
        MessageBoxW(nullptr, L"InitGpuResources Failed", L"Scene::Scene",MB_OK);
        throw std::runtime_error{"InitGpuResources Failed. from Scene::Scene"};
    }
}
void Scene::UpdateCBuffer(ID3D11DeviceContext *ctx) {
    // 设置Scene CB 到 b0 槽位
    ComputeCurSceneCB();
    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_Data, sizeof(m_CB_Data));
        ctx->Unmap(m_CBuffer.Get(), 0);
    } else {
        MessageBoxW(nullptr, L"Map cbuffer failed", L"Scene::Draw_Update",MB_OK);
    }

    ctx->VSSetConstantBuffers(Z::RenderBindings::SceneCB, 1, m_CBuffer.GetAddressOf());
    ctx->PSSetConstantBuffers(Z::RenderBindings::SceneCB, 1, m_CBuffer.GetAddressOf());
}


[[nodiscard]] DirectX::XMFLOAT3 Scene::GetCameraForward() const {
    DirectX::XMFLOAT3 forward{};
    forward.x = std::cos(m_state.pitch) * std::sin(m_state.yaw);
    forward.y = std::sin(m_state.pitch);
    forward.z = std::cos(m_state.pitch) * std::cos(m_state.yaw);
    return forward;
}
DirectX::XMMATRIX Scene::GetCameraView() const {
    // return DirectX::XMLoadFloat4x4(&m_CB_Data.view); //[sub]
    DirectX::XMFLOAT4X4 view{};
    ComputeViewMatrix(&view);
    return DirectX::XMLoadFloat4x4(&view);
}

DirectX::XMMATRIX Scene::BuildShadowCameraProj(float distance) const {
    return {DirectX::XMMatrixPerspectiveFovLH(
        m_state.fovY, m_state.aspectRatio,
        m_state.nearPlane, distance)};
}

void Scene::MoveCameraPos(DirectX::XMFLOAT3 dir, float deltaTime) {
    using namespace DirectX;
    constexpr float moveSpeed = 8.0f;
    XMFLOAT3 fwd = GetCameraForward();

    XMVECTOR worldUp = XMVectorSet(0, 1, 0, 0);

    XMVECTOR forward = XMLoadFloat3(&fwd);
    XMVECTOR right = XMVector3Normalize(XMVector3Cross(worldUp, forward));
    XMVECTOR up = XMVector3Cross(forward, right);

    XMVECTOR moveWorld = right * dir.x + up * dir.y + forward * dir.z;
    moveWorld *= moveSpeed * deltaTime;

    XMVECTOR curWorldPos = XMLoadFloat3(&m_state.cameraPos);
    curWorldPos += moveWorld;

    XMStoreFloat3(&m_state.cameraPos, curWorldPos);
}

// 添加一个渲染物体
int Scene::AddRenderObj(SharedModel *model, ID3D11Device *device) {
    //获取默认shader
    Shader *shader = Shader::S_GetDefaultShader(device);

    //获取默认Material
    Material *defMat = Material::S_CreateMaterial("Default", shader, device);
    if (!defMat) return -1;

    //创建RenderObject
    auto *obj = RenderObject::S_CreateRenderObj(model, defMat, device);

    if (!obj) { return -1; }

    ar_RenderObj.push_back(obj);
    return static_cast<int>(ar_RenderObj.size() - 1);
}

void Scene::SetRenderObjMatrix(int id, const DirectX::XMFLOAT4X4 &matrix) const {
    ar_RenderObj[id]->SetObjWorldMatrix(matrix);

}
bool Scene::SetRenderObjMaterial(int objId, int meshId, Material *mat) const {
    if (0 <= objId && objId < ar_RenderObj.size()) {
        return ar_RenderObj[objId]->SetMeshMaterial(meshId, mat);
    } else {
        MessageBoxW(nullptr, L"scene", L"ar_RenderObj越界",MB_OK);
        return false;
    }
}

//[add]
void Scene::SetDirectionalLight(const DirectX::XMFLOAT3 &direction,
                                const DirectX::XMFLOAT3 &color, float intensity) noexcept {
    const DirectX::XMVECTOR directionVector = DirectX::XMLoadFloat3(&direction);
    if (DirectX::XMVectorGetX(DirectX::XMVector3LengthSq(directionVector)) > 1.0e-8f) {
        DirectX::XMStoreFloat3(&m_state.lightDir, DirectX::XMVector3Normalize(directionVector));
    }
    m_state.lightColor = color;
    m_state.lightIntensity = std::max(intensity, 0.0f);
}

//[add]
void Scene::SetAmbientLight(const DirectX::XMFLOAT3 &color, float intensity) noexcept {
    m_state.ambientColor = color;
    m_state.ambientIntensity = std::max(intensity, 0.0f);
}


//= private
bool Scene::InitGpuResources(ID3D11Device *device) {
    D3D11_BUFFER_DESC desc{};
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.ByteWidth = (sizeof(SceneData) + 15u) & ~15u;
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    HRESULT hr = device->CreateBuffer(&desc, nullptr, m_CBuffer.GetAddressOf());
    if (FAILED(hr)) {
        MessageBoxW(nullptr, L"Create CBuffer failed.", L"Scene::InitGpuResources",MB_OK);
        return false;
    }
    return true;
}

void Scene::ComputeCurSceneCB() {
    ComputeViewMatrix(&m_CB_Data.view);
    ComputeProjMatrix(&m_CB_Data.proj);

    const auto &camera = m_state.cameraPos;
    m_CB_Data.cameraPos = {camera.x, camera.y, camera.z, 1.0f};

    const auto &direction = m_state.lightDir;
    m_CB_Data.lightDirection = {direction.x, direction.y, direction.z, 0.0f};

    const auto &lightColor = m_state.lightColor;
    m_CB_Data.lightColorIntensity = {lightColor.x, lightColor.y,
        lightColor.z, m_state.lightIntensity};

    const auto &ambientColor = m_state.ambientColor;
    m_CB_Data.ambientColorIntensity = {ambientColor.x, ambientColor.y,
        ambientColor.z, m_state.ambientIntensity};

    m_CB_Data.renderParams = {std::max(m_state.exposure, 0.0f), 0.0f, 0.0f, 0.0f};

}

void Scene::ComputeViewMatrix(DirectX::XMFLOAT4X4 *pViewMatrix_out) const {
    // pos
    DirectX::FXMVECTOR eye(DirectX::XMLoadFloat3(&m_state.cameraPos));
    // dir,保险起见归一化一下,这里4维向量的w是0,表示这是方向向量,nor只用3维
    DirectX::XMFLOAT3 f = GetCameraForward();
    DirectX::XMVECTOR fwd = DirectX::XMVector3Normalize(DirectX::XMLoadFloat3(&f));
    // 世界上向(确定坐标系)
    DirectX::XMVECTOR up = DirectX::XMVectorSet(0, 1.0f, 0, 0);
    // 左手坐标系下生成V矩阵
    auto vm = DirectX::XMMatrixLookToLH(eye, fwd, up);
    // // 将XMMATRIX转置后存入XMFLOAT4X4,(HLSL里用列向量模式)
    // XMStoreFloat4x4(pViewMatrix_out, DirectX::XMMatrixTranspose(vm));
    // 将XMMATRIX直接存入XMFLOAT4X4,(HLSL里用行向量模式)
    XMStoreFloat4x4(pViewMatrix_out, vm);
}

void Scene::ComputeProjMatrix(DirectX::XMFLOAT4X4 *pProjMatrix_out) const {
    auto proj = DirectX::XMMatrixPerspectiveFovLH(
        m_state.fovY, m_state.aspectRatio,
        m_state.nearPlane, m_state.farPlane);
    // // 将XMMATRIX转置后存入XMFLOAT4X4,(HLSL里用列向量模式)
    // DirectX::XMStoreFloat4x4(pProjMatrix_out, DirectX::XMMatrixTranspose(proj));
    // 将XMMATRIX直接存入XMFLOAT4X4,(HLSL里用行向量模式)
    DirectX::XMStoreFloat4x4(pProjMatrix_out, proj);
}














//
