use super::*;

#[derive(Debug)]
pub struct Texture {
    handle: TextureHandle,
    pub image: vk::Image,
    pub image_view: vk::ImageView,
    pub sampler: vk::Sampler,
}

impl Texture {
    pub fn release(self) -> TextureHandle { self.handle }
}

impl Resource for Texture {
    type Allocated = AllocatedTexture;
    type Handle = TextureHandle;
    type Info = ();

    fn new(r: &Self::Allocated, handle: Self::Handle, info: Self::Info) -> Self {
        Self { handle, image: r.image.handle, image_view: r.image_view, sampler: r.sampler }
    }
}

pub type TextureHandle = GenericHandle<Texture>;

#[derive(Debug)]
pub struct AllocatedTexture {
    image: AllocatedImage,
    image_view: vk::ImageView,
    sampler: vk::Sampler,
}

#[derive(Debug)]
pub struct TextureStorage {
    data: StorageData<TextureId, AllocatedTexture>,
}

impl TextureStorage {

    pub fn new() -> Self {
        Self { data: StorageData::new() }
    }

    pub fn destroy(&mut self, log: &Logger, device: &Device, allocator: &Allocator) {
        self.data.clear(|allocated| {

            unsafe { device.destroy_sampler(allocated.sampler, None); }

            unsafe { device.destroy_image_view(allocated.image_view, None); }

            allocator.destroy_image(allocated.image.handle,
                                    &allocated.image.allocation)
                .expect_or_log(log, "unable to destroy image");
        })
    }
}

impl Storage for TextureStorage {

    type Id = TextureId;
    type Resource = Texture;
    type ResourceInfo = ();
    type Allocated = AllocatedTexture;
    type Handle = TextureHandle;
    type HandleInfo = ();

    fn data(&self) -> &StorageData<Self::Id, Self::Allocated> { &self.data }

    fn load(&self, r: &Resources, id: Self::Id, info: &Self::HandleInfo) -> Self::Allocated {

        let log = r.log.new(log::o!("texture" => id));

        let texture = get_texture(id).expect_or_log(&log, "unable to load texture");
        let data_size = texture.image_data().len();
        let (width, height) = texture.dimensions();

        let image = create_image(
            r, ImageType::Texture2D,
            vk::Extent2D { width, height },
            &r.image_formats);
        
        copy_data_to_image(
            &log, r, &image, texture.dimensions(), data_size,
            |data_ptr| unsafe { copy_to_ptr(texture.image_data(), data_ptr); });

        let image_view = {
            let create_info = vk::ImageViewCreateInfo::builder()
                .image(image.handle)
                .view_type(vk::ImageViewType::TYPE_2D)
                .format(vk::Format::R8G8B8A8_SRGB)
                .subresource_range(vk::ImageSubresourceRange {
                    aspect_mask: vk::ImageAspectFlags::COLOR,
                    base_mip_level: 0,
                    level_count: 1,
                    base_array_layer: 0,
                    layer_count: 1
                })
                .components(vk::ComponentMapping {
                    r: vk::ComponentSwizzle::IDENTITY,
                    g: vk::ComponentSwizzle::IDENTITY,
                    b: vk::ComponentSwizzle::IDENTITY,
                    a: vk::ComponentSwizzle::IDENTITY
                });
            unsafe { r.device.create_image_view(&create_info, None) }
                .expect_or_log(&log, "unable to create image view")
        };

        let sampler = {
            let create_info = vk::SamplerCreateInfo::builder()
                .mag_filter(vk::Filter::LINEAR)
                .min_filter(vk::Filter::LINEAR)
                .address_mode_u(vk::SamplerAddressMode::MIRRORED_REPEAT)
                .address_mode_v(vk::SamplerAddressMode::MIRRORED_REPEAT)
                .address_mode_w(vk::SamplerAddressMode::MIRRORED_REPEAT)
                .anisotropy_enable(true)
                .max_anisotropy(16.0)
                .border_color(vk::BorderColor::INT_OPAQUE_BLACK)
                .unnormalized_coordinates(false)
                .compare_enable(false)
                .compare_op(vk::CompareOp::ALWAYS)
                .mipmap_mode(vk::SamplerMipmapMode::LINEAR)
                .mip_lod_bias(0.0)
                .min_lod(0.0)
                .max_lod(0.0);
            unsafe { r.device.create_sampler(&create_info, None) }
                .expect_or_log(&log, "unable to create sampler")
        };
        
        AllocatedTexture { image, image_view, sampler }
    }
}
