#include "graphic.h"
#include <tool.h>
#include <array>
#include <iostream>
#include "render_object.h"

//= public
bool Graphic::Init(HWND outputWnd) {
    //region CreateDevice

    HRESULT hr{};

    UINT flag = 0;
#ifdef _DEBUG
    flag |= D3D11_CREATE_DEVICE_DEBUG;
#endif
    std::array candidate_levels = {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };
    std::array fallback_levels = {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };

    DXGI_SWAP_CHAIN_DESC desc{};
    desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc.BufferDesc.Width = 0;
    desc.BufferDesc.Height = 0;
    desc.BufferDesc.RefreshRate = DXGI_RATIONAL(0, 0); //窗口化给0 0,让DWM接管
    desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc.BufferCount = 2;
    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc.SampleDesc = DXGI_SAMPLE_DESC(1, 0); //Flip模型不支持msaa
    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    desc.Windowed = TRUE;
    desc.Flags = 0;
    desc.OutputWindow = outputWnd;


    auto create_device = [&](UINT _flag, const D3D_FEATURE_LEVEL *arr,
                             UINT size) -> HRESULT {
        return D3D11CreateDeviceAndSwapChain(
            nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, _flag, arr,
            size,D3D11_SDK_VERSION, &desc,
            m_swapchain.GetAddressOf(), m_device.GetAddressOf(),
            &m_featureLevel, m_device_ctx.GetAddressOf());
    };

    auto try_create_device = [&](UINT sel_flag) -> HRESULT {
        hr = create_device(sel_flag, candidate_levels.data(), candidate_levels.size());
        if (FAILED(hr) && hr == E_INVALIDARG)
            hr = create_device(sel_flag, fallback_levels.data(), fallback_levels.size());
        return hr;
    };

    hr = try_create_device(flag);

#ifdef _DEBUG
    if (FAILED(hr)) {
        flag &= ~D3D11_CREATE_DEVICE_DEBUG;
        hr = try_create_device(flag);
    }
#endif

    if (FAILED(hr)) {
        std::wstring msg = Z::HResultToWString(hr);
        MessageBoxW(nullptr, msg.c_str(),
                    L"D3D11CreateDeviceAndSwapChain",MB_OK);
        return false;
    }
    //endregion

    if (!CreateBackBufferResources()) return false;

    if (!CreateRasterizerState())return false;

    return true;
}

bool Graphic::ResizeTheWinodw(UINT w, UINT h) {
    DestroyBackBufferResources();
    HRESULT hr = m_swapchain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0);
    if (FAILED(hr)) {
        std::wstring msg = Z::HResultToWString(hr);
        MessageBoxW(nullptr, msg.c_str(), L"ReszieBuffer failed",MB_OK);
        return false;
    }
    return CreateBackBufferResources();
}

void Graphic::BeginMainPass() {
    m_device_ctx->OMSetRenderTargets(1, m_rtv.GetAddressOf(), m_dsv.Get());
    m_device_ctx->RSSetViewports(1, m_viewPorts);
    m_device_ctx->RSSetState(m_rasterizer_state.Get());
    m_device_ctx->OMSetDepthStencilState(m_ds_state.Get(), 0);
    m_device_ctx->ClearRenderTargetView(m_rtv.Get(), m_clearColor);
    m_device_ctx->ClearDepthStencilView(
        m_dsv.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    m_device_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

}
void Graphic::EndMainPass() {
    //TODO EndMainPass

}

void Graphic::Present() const {
    (void) m_swapchain->Present(1, 0);
}




//= PRIVATE
bool Graphic::CreateBackBufferResources() {
    HRESULT hr{};
    // 获取RT BUFFER
    hr = m_swapchain->GetBuffer(0, IID_PPV_ARGS(m_back_buffer.GetAddressOf()));
    if (FAILED(hr)) {
        std::wstring msg = Z::HResultToWString(hr);
        MessageBoxW(nullptr, msg.c_str(), L"GetBuffer of RTV",MB_OK);
        return false;
    }
    // 创建RTV
    hr = m_device->CreateRenderTargetView(m_back_buffer.Get(), nullptr,
                                          m_rtv.GetAddressOf());

    if (FAILED(hr)) {
        std::wstring msg = Z::HResultToWString(hr);
        MessageBoxW(nullptr, msg.c_str(), L"CreateRTV",MB_OK);
        return false;
    }

    // 创建DS BUFFER
    D3D11_TEXTURE2D_DESC rtd{};
    m_back_buffer->GetDesc(&rtd);

    D3D11_TEXTURE2D_DESC dsd{};
    dsd.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    dsd.CPUAccessFlags = DXGI_CPU_ACCESS_NONE;
    dsd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    dsd.Width = rtd.Width;
    dsd.Height = rtd.Height;
    dsd.ArraySize = 1;
    dsd.Usage = D3D11_USAGE_DEFAULT;
    dsd.MipLevels = 1;
    dsd.SampleDesc = rtd.SampleDesc;

    hr = m_device->CreateTexture2D(&dsd, nullptr, m_ds_buffer.GetAddressOf());
    if (FAILED(hr)) {
        std::wstring msg = Z::HResultToWString(hr);
        MessageBoxW(nullptr, msg.c_str(), L"Create DS Buffer",MB_OK);
        return false;
    }
    // 创建DSV
    hr = m_device->CreateDepthStencilView(m_ds_buffer.Get(), nullptr,
                                          m_dsv.GetAddressOf());
    if (FAILED(hr)) {
        std::wstring msg = Z::HResultToWString(hr);
        MessageBoxW(nullptr, msg.c_str(), L"Create DSV",MB_OK);
        return false;
    }
    // 创建 DepthStencil State
    D3D11_DEPTH_STENCIL_DESC _dsd{};
    _dsd.DepthEnable = TRUE;
    _dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    _dsd.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
    _dsd.StencilEnable = FALSE;

    hr = m_device->CreateDepthStencilState(&_dsd, m_ds_state.ReleaseAndGetAddressOf());
    if (FAILED(hr)) {
        std::wstring msg = Z::HResultToWString(hr);
        MessageBoxW(nullptr, msg.c_str(), L"Create DS State",MB_OK);
        return false;
    }

    // 设置视口
    m_viewPorts[0].Width = static_cast<FLOAT>(rtd.Width);
    m_viewPorts[0].Height = static_cast<FLOAT>(rtd.Height);
    m_viewPorts[0].MaxDepth = 1.0f;
    m_viewPorts[0].MinDepth = 0.0f;

    m_device_ctx->RSSetViewports(1, m_viewPorts);
    m_device_ctx->OMSetRenderTargets(1, m_rtv.GetAddressOf(), m_dsv.Get());

    return true;
}
void Graphic::DestroyBackBufferResources() {
    if (!m_device.Get() || !m_swapchain.Get()) return;
    //1 解绑RTV DSV
    ID3D11RenderTargetView *nullRTV = nullptr;
    m_device_ctx->OMSetRenderTargets(1, &nullRTV, nullptr);
    //2 释放rtv dsv backbuffer
    if (m_rtv.Get()) m_rtv.Reset();
    if (m_dsv.Get()) m_dsv.Reset();
    if (m_back_buffer.Get()) m_back_buffer.Reset();

}
bool Graphic::CreateRasterizerState() {
    D3D11_RASTERIZER_DESC desc{};
    desc.FillMode = D3D11_FILL_SOLID;
    desc.CullMode = D3D11_CULL_NONE;
    desc.FrontCounterClockwise = false;
    desc.DepthClipEnable = true;
    desc.MultisampleEnable = false;
    desc.ScissorEnable = false;
    HRESULT hr = m_device->CreateRasterizerState(
        &desc, m_rasterizer_state.GetAddressOf());
    if (FAILED(hr)) {
        MessageBoxW(nullptr, L"Create Rasterizer State Failed.", L"Graphic Init",MB_OK);
        return false;
    }
    return true;
}


















//
