use std::cmp::min;
use crate::{
    command::FamilyId,
    memory::{DynamicConfig, HeapsConfig, LinearConfig},
    util::DeviceId,
};
#[derive(Clone, derivative::Derivative)]
#[derivative(Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Config<D = BasicDevicesConfigure, H = BasicHeapsConfigure, Q = OneGraphicsQueue> {
    
    pub devices: D,
    
    pub heaps: H,
    
    pub queues: Q,
}
pub unsafe trait QueuesConfigure {
    
    type Priorities: AsRef<[f32]>;
    
    type Families: IntoIterator<Item = (FamilyId, Self::Priorities)>;
    
    fn configure(
        self,
        device: DeviceId,
        families: &[impl gfx_hal::queue::QueueFamily],
    ) -> Self::Families;
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OneGraphicsQueue;
unsafe impl QueuesConfigure for OneGraphicsQueue {
    type Priorities = [f32; 1];
    type Families = Option<(FamilyId, [f32; 1])>;
    fn configure(
        self,
        device: DeviceId,
        families: &[impl gfx_hal::queue::QueueFamily],
    ) -> Option<(FamilyId, [f32; 1])> {
        families
            .iter()
            .find(|f| f.supports_graphics() && f.max_queues() > 0)
            .map(|f| {
                (
                    FamilyId {
                        device,
                        index: f.id().0,
                    },
                    [1.0],
                )
            })
    }
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SavedQueueConfig(Vec<(usize, Vec<f32>)>);
unsafe impl QueuesConfigure for SavedQueueConfig {
    type Priorities = Vec<f32>;
    type Families = Vec<(FamilyId, Vec<f32>)>;
    fn configure(
        self,
        device: DeviceId,
        _: &[impl gfx_hal::queue::QueueFamily],
    ) -> Vec<(FamilyId, Vec<f32>)> {
        
        self.0
            .into_iter()
            .map(|(id, vec)| (FamilyId { device, index: id }, vec))
            .collect()
    }
}
pub unsafe trait HeapsConfigure {
    
    type Types: IntoIterator<Item = (gfx_hal::memory::Properties, u32, HeapsConfig)>;
    
    type Heaps: IntoIterator<Item = u64>;
    
    fn configure(
        self,
        properties: &gfx_hal::adapter::MemoryProperties,
    ) -> (Self::Types, Self::Heaps);
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BasicHeapsConfigure;
unsafe impl HeapsConfigure for BasicHeapsConfigure {
    type Types = Vec<(gfx_hal::memory::Properties, u32, HeapsConfig)>;
    type Heaps = Vec<u64>;
    fn configure(
        self,
        properties: &gfx_hal::adapter::MemoryProperties,
    ) -> (Self::Types, Self::Heaps) {
        let _1mb = 1024 * 1024;
        let _32mb = 32 * _1mb;
        let _128mb = 128 * _1mb;
        let types = properties
            .memory_types
            .iter()
            .map(|mt| {
                let config = HeapsConfig {
                    linear: if mt
                        .properties
                        .contains(gfx_hal::memory::Properties::CPU_VISIBLE)
                    {
                        Some(LinearConfig {
                            linear_size: min(_128mb, properties.memory_heaps[mt.heap_index] / 16),
                        })
                    } else {
                        None
                    },
                    dynamic: Some(DynamicConfig {
                        block_size_granularity: 256.min(
                            (properties.memory_heaps[mt.heap_index] / 4096).next_power_of_two(),
                        ),
                        min_device_allocation: _1mb
                            .min(properties.memory_heaps[mt.heap_index] / 1048)
                            .next_power_of_two(),
                        max_chunk_size: _32mb.min(
                            (properties.memory_heaps[mt.heap_index] / 128).next_power_of_two(),
                        ),
                    }),
                };
                (mt.properties, mt.heap_index as u32, config)
            })
            .collect();
        let heaps = properties.memory_heaps.iter().cloned().collect();
        (types, heaps)
    }
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SavedHeapsConfig {
    types: Vec<(gfx_hal::memory::Properties, u32, HeapsConfig)>,
    heaps: Vec<u64>,
}
unsafe impl HeapsConfigure for SavedHeapsConfig {
    type Types = Vec<(gfx_hal::memory::Properties, u32, HeapsConfig)>;
    type Heaps = Vec<u64>;
    fn configure(
        self,
        _properties: &gfx_hal::adapter::MemoryProperties,
    ) -> (Self::Types, Self::Heaps) {
        (self.types, self.heaps)
    }
}
pub trait DevicesConfigure {
    
    
    
    
    
    
    fn pick<B>(&self, adapters: &[gfx_hal::Adapter<B>]) -> usize
    where
        B: gfx_hal::Backend;
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BasicDevicesConfigure;
impl DevicesConfigure for BasicDevicesConfigure {
    fn pick<B>(&self, adapters: &[gfx_hal::Adapter<B>]) -> usize
    where
        B: gfx_hal::Backend,
    {
        adapters
            .iter()
            .enumerate()
            .min_by_key(|(_, adapter)| match adapter.info.device_type {
                gfx_hal::adapter::DeviceType::DiscreteGpu => 0,
                gfx_hal::adapter::DeviceType::IntegratedGpu => 1,
                gfx_hal::adapter::DeviceType::VirtualGpu => 2,
                gfx_hal::adapter::DeviceType::Cpu => 3,
                _ => 4,
            })
            .expect("No adapters present")
            .0
    }
}