use {
    crate::{
        gfx_api::GfxBlendBuffer,
        gfx_apis::vulkan::{
            VulkanError,
            format::{BLEND_FORMAT, BLEND_USAGE},
            image::{QueueFamily, QueueState, VulkanImage, VulkanImageMemory},
            renderer::VulkanRenderer,
        },
    },
    ash::vk::{
        Extent3D, ImageAspectFlags, ImageCreateInfo, ImageLayout, ImageSubresourceRange,
        ImageTiling, ImageType, ImageViewCreateInfo, ImageViewType, SampleCountFlags, SharingMode,
    },
    gpu_alloc::UsageFlags,
    run_on_drop::on_drop,
    std::{cell::Cell, collections::hash_map::Entry, rc::Rc},
};

impl VulkanRenderer {
    pub fn acquire_blend_buffer(
        self: &Rc<Self>,
        width: i32,
        height: i32,
    ) -> Result<Rc<VulkanImage>, VulkanError> {
        if self.device.descriptor_buffer.is_none() {
            return Err(VulkanError::NoDescriptorBuffer);
        }
        if width <= 0 || height <= 0 {
            return Err(VulkanError::NonPositiveImageSize);
        }
        let width = width as u32;
        let height = height as u32;
        let cached = &mut *self.blend_buffers.borrow_mut();
        let cached = cached.entry((width, height));
        if let Entry::Occupied(entry) = &cached
            && let Some(buffer) = entry.get().upgrade()
        {
            return Ok(buffer);
        }
        let limits = self.device.blend_limits;
        if width > limits.max_width || height > limits.max_height {
            return Err(VulkanError::ImageTooLarge);
        }
        let usage = BLEND_USAGE;
        let create_info = ImageCreateInfo::default()
            .image_type(ImageType::TYPE_2D)
            .format(BLEND_FORMAT.vk_format)
            .mip_levels(1)
            .array_layers(1)
            .tiling(ImageTiling::OPTIMAL)
            .samples(SampleCountFlags::TYPE_1)
            .sharing_mode(SharingMode::EXCLUSIVE)
            .initial_layout(ImageLayout::UNDEFINED)
            .extent(Extent3D {
                width,
                height,
                depth: 1,
            })
            .usage(usage);
        let image = unsafe { self.device.device.create_image(&create_info, None) };
        let image = image.map_err(VulkanError::CreateImage)?;
        let destroy_image = on_drop(|| unsafe { self.device.device.destroy_image(image, None) });
        let memory_requirements =
            unsafe { self.device.device.get_image_memory_requirements(image) };
        let allocation =
            self.allocator
                .alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
        let res = unsafe {
            self.device
                .device
                .bind_image_memory(image, allocation.memory, allocation.offset)
        };
        res.map_err(VulkanError::BindImageMemory)?;
        let image_view_create_info = ImageViewCreateInfo::default()
            .image(image)
            .format(BLEND_FORMAT.vk_format)
            .view_type(ImageViewType::TYPE_2D)
            .subresource_range(ImageSubresourceRange {
                aspect_mask: ImageAspectFlags::COLOR,
                base_mip_level: 0,
                level_count: 1,
                base_array_layer: 0,
                layer_count: 1,
            });
        let view = unsafe {
            self.device
                .device
                .create_image_view(&image_view_create_info, None)
        };
        let view = view.map_err(VulkanError::CreateImageView)?;
        destroy_image.forget();
        let img = Rc::new(VulkanImage {
            renderer: self.clone(),
            format: BLEND_FORMAT,
            width,
            height,
            stride: 0,
            texture_view: view,
            render_view: None,
            image,
            is_undefined: Cell::new(true),
            contents_are_undefined: Cell::new(true),
            queue_state: Cell::new(QueueState::Acquired {
                family: QueueFamily::Gfx,
            }),
            ty: VulkanImageMemory::Blend(allocation),
            bridge: None,
            sampled_image_descriptor: self.sampled_image_descriptor(usage, view),
            execution_version: Default::default(),
        });
        cached.insert_entry(Rc::downgrade(&img));
        Ok(img)
    }
}

impl GfxBlendBuffer for VulkanImage {}
