#include "dx_window.h"

#include <spdlog/spdlog.h>

#include "dx_renderer.h"
#include "core/pixel_format/pixel.h"
#include "core/renderer/renderer_text.h"
#include "core/renderer/renderer_texture.h"
#include "misc/scope_exit.h"

using namespace aorii;

renderer_texture* test_texture = nullptr;
// text_font* text = nullptr;
wchar_t current_char = u'a';
void delta_char_texture(int in_delta) {
    current_char -= in_delta;
    // current_char = std::clamp(current_char, 33, 126);
    // const auto& character = text->glyphs[current_char];
    // test_texture = character.glyph_texture;
}
void on_mouse_scroll(GLFWwindow* window, double xoffset, double yoffset) {
    delta_char_texture(yoffset);
}

bool dx_window::create_surface(GLFWwindow* in_window) {
    auto dx = aorii::get_renderer<dx_renderer>();
    const auto d3d_device = dx->get_d3d_device();

    IDXGIDevice* dxgi_device = nullptr;
    auto hr = d3d_device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgi_device));
    if (FAILED(hr)) {
        spdlog::critical("无法获取IDXGIDevice, 错误: {0}", hr);
        return false;
    }
    ON_SCOPE_EXIT{
        if (dxgi_device) dxgi_device->Release();
    };

    IDXGIAdapter* dxgi_adapter = nullptr;
    hr = dxgi_device->GetAdapter(&dxgi_adapter);
    if (FAILED(hr)) {
        spdlog::critical("无法获取IDXGIAdapter, 错误: {0}", hr);
        return false;
    }
    ON_SCOPE_EXIT{
        if (dxgi_adapter) dxgi_adapter->Release();
    };

    IDXGIFactory2* dxgi_factory = nullptr;
    hr = dxgi_adapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgi_factory));
    if (FAILED(hr)) {
        spdlog::critical("无法获取IDXGIFactory, 错误: {0}", hr);
        return false;
    }
    ON_SCOPE_EXIT{
        if (dxgi_factory) dxgi_factory->Release();
    };

    DXGI_SWAP_CHAIN_DESC1 sd = {};
    sd.Width = 0; // 使用窗口宽度
    sd.Height = 0; // 使用窗口高度
    sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.BufferCount = 2;
    sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    sd.Flags = 0;

    const auto hwnd = static_cast<HWND>(get_window_handle());
    hr = dxgi_factory->CreateSwapChainForHwnd(d3d_device, hwnd, &sd, nullptr, nullptr, &swap_chain);
    if (FAILED(hr)) {
        spdlog::critical("无法创建交换链, 错误: {0}", hr);
        return false;
    }

    context.init();

    hr = build_render_target_view();
    if (FAILED(hr)) {
        spdlog::critical("无法创建渲染模板视图, 错误: {0}", hr);
        return false;
    }

    // test_texture = dx->load_image(R"(D:\69054578_p0.jpg)", texture_format::RGBA8_UNORM);
    glfwSetScrollCallback(get_glfw_window(), on_mouse_scroll);
    // text = aorii_text::load_font("C:/Windows/Fonts/simsunb.ttf", 32);
    // delta_char_texture(0);

    return true;
}

void dx_window::begin_frame() {
    glfwMakeContextCurrent(get_glfw_window());
    const auto render = aorii::get_renderer<dx_renderer>();
    const auto d3d_context = render->get_d3d_context();

    d3d_context->OMSetRenderTargets(1, &render_target_view, nullptr);
    d3d_context->OMSetBlendState(render->get_blend_state(), nullptr, 0xffffffff);
    d3d_context->ClearRenderTargetView(render_target_view, clear_color);
    context.set_projection_matrix(projection_matrix, get_framebuffer_size());

    context.draw_rectangle({ 400, 0 }, { 100, 100 }, { 0, 1, 0, 1 });
    auto radius = abs(sin(get_total_time().count())) * 50;
    auto angle = sin(get_total_time().count()) * 45;
    context.draw_rounded_rectangle({ 500, 500 }, { 100, 400 }, { 1, 0, 0, 1 }, angle, {10, 20, 30, 40});

    double mouse_x, mouse_y;
    glfwGetCursorPos(get_glfw_window(), &mouse_x, &mouse_y);
    // auto height = sin(get_total_time().count()) * 100;
    // context.draw_line( { 600, 600 }, { mouse_x, mouse_y }, { 1, 0, 1, 1 }, thickness);

    // if (test_texture) context.draw_texture({ 0.f, 0.f }, test_texture->size().cast<float>(), test_texture);
    // context.draw_string({0, 0}, U"你好，世界！全是水群大师\n测试换行\n测试Unicode: 😀\nТест по русскому языку\nテスト日本語", 32, {0, 0, 0, 1});
    float font_height = 64;
    context.draw_string({0, 100}, U"你好，世界！\nТест по русскому языку\nテスト", font_height, {0, 0, 0, 1});
    context.draw_rectangle({ 0, 100 }, { 2048, 1 }, { 1, 0, 1, 1 });
    context.draw_rectangle({ 0, 100 + font_height }, { 2048, 1 }, { 1, 0, 1, 1 });
    context.draw_rectangle({ 0, 100 + font_height * 2 }, { 2048, 1 }, { 1, 0, 1, 1 });
    context.draw_rectangle({ 0, 100 + font_height * 3 }, { 2048, 1 }, { 1, 0, 1, 1 });

    context.flush();

    swap_chain->Present(1, 0);
}

void dx_window::end_frame() {

}

void dx_window::on_resize(const Eigen::Vector2i& in_size) {
    if (in_size.isZero())
        return;
    if (render_target_view) {
        render_target_view->Release();
        render_target_view = nullptr;
    }

    auto hr = swap_chain->ResizeBuffers(0, in_size.x(), in_size.y(), DXGI_FORMAT_UNKNOWN, 0);
    if (FAILED(hr)) {
        spdlog::critical("Failed to resize window. Error: {0}", hr);
        return;
    }
    build_render_target_view();
}

HRESULT dx_window::build_render_target_view() {
    const auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
    const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();

    ID3D11Texture2D* back_buffer = nullptr;
    auto hr = swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&back_buffer);
    if (FAILED(hr)) {
        spdlog::critical("Failed to get back buffer. Error: {0}", hr);
        return hr;
    }
    ON_SCOPE_EXIT{
        if (back_buffer) back_buffer->Release();
    };

    hr = d3d_device->CreateRenderTargetView(back_buffer, nullptr, &render_target_view);
    if (FAILED(hr)) {
        spdlog::critical("Failed to create render target. Error: {0}", hr);
        return hr;
    }

    auto framebuffer_size = get_framebuffer_size();

    D3D11_VIEWPORT viewport = {};
    viewport.Width = (float)framebuffer_size.x();
    viewport.Height = (float)framebuffer_size.y();
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    viewport.TopLeftX = 0.0f;
    viewport.TopLeftY = 0.0f;
    d3d_context->RSSetViewports(1, &viewport);

    projection_matrix = get_projection_matrix().transpose();

    return hr;
}
