// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use windows::Win32::Graphics::Direct3D12::{D3D12_RESOURCE_STATE_PRESENT, ID3D12Resource};
use windows::Win32::Graphics::Dxgi::Common::DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN;
use windows::Win32::Graphics::Dxgi::Common::{
    DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
};

use wgpu_29 as wgpu;

/// # Safety
/// `resource` must be a valid D3D12 resource for the lifetime of the returned Surface.
unsafe fn wrap_dx12_texture(
    width: i32,
    height: i32,
    gr_context: &mut skia_safe::gpu::DirectContext,
    resource: ID3D12Resource,
    dxgi_format: windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT,
    color_type: skia_safe::ColorType,
    color_space: skia_safe::ColorSpace,
) -> Option<skia_safe::Surface> {
    unsafe {
        let texture_info = skia_safe::gpu::d3d::TextureResourceInfo {
            resource,
            alloc: None,
            resource_state: D3D12_RESOURCE_STATE_PRESENT,
            format: dxgi_format,
            sample_count: 1,
            level_count: 1,
            sample_quality_pattern: DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN,
            protected: skia_safe::gpu::Protected::No,
        };
        let backend_render_target =
            skia_safe::gpu::BackendRenderTarget::new_d3d((width, height), &texture_info);
        skia_safe::gpu::surfaces::wrap_backend_render_target(
            gr_context,
            &backend_render_target,
            skia_safe::gpu::SurfaceOrigin::TopLeft,
            color_type,
            color_space,
            None,
        )
    }
}

/// # Safety
/// The caller must ensure `texture` was created by a DX12-backed wgpu device and remains
/// valid for the lifetime of the returned `skia_safe::Surface`.
pub unsafe fn make_dx12_surface(
    gr_context: &mut skia_safe::gpu::DirectContext,
    texture: &wgpu::Texture,
) -> Option<skia_safe::Surface> {
    // SAFETY: texture is borrowed for the duration of this call; the D3D12 resource is
    // ref-counted (COM) and cloned into Skia's internal BackendRenderTarget via wrap_dx12_texture.
    unsafe {
        let dx12_texture = texture.as_hal::<wgpu::wgc::api::Dx12>()?;
        let resource = windows_core::Interface::from_raw(windows_core::Interface::into_raw(
            dx12_texture.raw_resource().clone(),
        ));
        let size = texture.size();
        let (dxgi_format, color_type) = match texture.format() {
            wgpu::TextureFormat::Rgba8Unorm => {
                (DXGI_FORMAT_R8G8B8A8_UNORM, skia_safe::ColorType::RGBA8888)
            }
            wgpu::TextureFormat::Rgba8UnormSrgb => {
                (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, skia_safe::ColorType::SRGBA8888)
            }
            wgpu::TextureFormat::Bgra8Unorm => {
                (DXGI_FORMAT_B8G8R8A8_UNORM, skia_safe::ColorType::BGRA8888)
            }
            _ => return None,
        };
        wrap_dx12_texture(
            size.width as i32,
            size.height as i32,
            gr_context,
            resource,
            dxgi_format,
            color_type,
            super::attachment_color_space(texture),
        )
    }
}

#[allow(non_snake_case)]
pub unsafe fn import_dx12_texture(
    canvas: &skia_safe::Canvas,
    texture: wgpu::Texture,
) -> Option<skia_safe::Image> {
    unsafe {
        let color_space = super::sampled_texture_color_space(&texture);
        let dx12_texture = texture.as_hal::<wgpu::wgc::api::Dx12>();

        let resource: ID3D12Resource = windows_core::Interface::from_raw(
            windows_core::Interface::into_raw(dx12_texture.unwrap().raw_resource().clone()),
        );

        let dxgi_texture_format = resource.GetDesc().Format;

        let color_type = match dxgi_texture_format {
            DXGI_FORMAT_R8G8B8A8_UNORM => skia_safe::ColorType::RGBA8888,
            DXGI_FORMAT_R8G8B8A8_UNORM_SRGB => skia_safe::ColorType::SRGBA8888,
            DXGI_FORMAT_B8G8R8A8_UNORM => skia_safe::ColorType::BGRA8888,
            _ => return None,
        };

        let texture_info = skia_safe::gpu::d3d::TextureResourceInfo {
            resource,
            alloc: None,
            resource_state: D3D12_RESOURCE_STATE_PRESENT,
            format: dxgi_texture_format,
            sample_count: 1,
            level_count: 1,
            sample_quality_pattern: DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN,
            protected: skia_safe::gpu::Protected::No,
        };
        let size = texture.size();

        let backend_texture = skia_safe::gpu::BackendTexture::new_d3d(
            (size.width as i32, size.height as i32),
            &texture_info,
        );

        Some(
            skia_safe::image::Image::from_texture(
                canvas.recording_context().as_mut().unwrap(),
                &backend_texture,
                skia_safe::gpu::SurfaceOrigin::TopLeft,
                color_type,
                skia_safe::AlphaType::Unpremul,
                color_space,
            )
            .unwrap(),
        )
    }
}

pub unsafe fn make_dx12_context(
    adapter: &wgpu::Adapter,
    _device: &wgpu::Device,
    queue: &wgpu::Queue,
) -> Option<skia_safe::gpu::DirectContext> {
    let backend = unsafe {
        let maybe_dx12_queue = queue.as_hal::<wgpu::wgc::api::Dx12>();
        let dx12_adapter = adapter.as_hal::<wgpu::wgc::api::Dx12>().unwrap();

        maybe_dx12_queue.map(|dx12_queue| {
            let dx12_queue_raw = dx12_queue.as_raw();
            let mut dx12_device_old: Option<windows::Win32::Graphics::Direct3D12::ID3D12Device> =
                None;
            dx12_queue_raw.GetDevice(&mut dx12_device_old as _).unwrap();
            let dx12_device_old = dx12_device_old.unwrap();
            let dx12_device = windows_core::Interface::from_raw(windows_core::Interface::into_raw(
                dx12_device_old,
            ));

            let idxgiadapter_3: windows::Win32::Graphics::Dxgi::IDXGIAdapter3 =
                dx12_adapter.as_raw().clone().into();

            skia_safe::gpu::d3d::BackendContext {
                adapter: windows_core::Interface::from_raw(windows_core::Interface::into_raw(
                    idxgiadapter_3,
                )),
                device: dx12_device,
                queue: windows_core::Interface::from_raw(windows_core::Interface::into_raw(
                    dx12_queue_raw.clone(),
                )),
                memory_allocator: None,
                protected_context: skia_safe::gpu::Protected::No,
            }
        })
    };

    skia_safe::gpu::DirectContext::new_d3d(&backend.unwrap(), None)
}
