use super::*;
use super::errors::*;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ImageBindlessId(u64);

impl ImageBindlessId {
    pub fn empty() -> ImageBindlessId {
        ImageBindlessId(0)
    }
}

impl From<ImageBindlessId> for u64 {
    fn from(image_bindless_id: ImageBindlessId) -> Self {
        image_bindless_id.0
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ImageAccess {
    Read,
    Write,
    ReadWrite,
}

impl From<ImageAccess> for u32 {
    fn from(access: ImageAccess) -> Self {
        match access {
            ImageAccess::Read => gl::READ_ONLY,
            ImageAccess::Write => gl::WRITE_ONLY,
            ImageAccess::ReadWrite => gl::READ_WRITE,
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ImageInternalFormat {
    R8,
    R8SN,
    R8I,
    R8UI,
    R16,
    R16F,
    R16SN,
    R16I,
    R16UI,
    R32F,
    R32I,
    R32UI,

    RG8,
    RG8SN,
    RG8I,
    RG8UI,
    RG16,
    RG16F,
    RG16SN,
    RG16I,
    RG16UI,
    RG32F,
    RG32I,
    RG32UI,

    RGBA8,
    RGBA8SN,
    RGBA8I,
    RGBA8UI,
    RGBA16,
    RGBA16F,
    RGBA16SN,
    RGBA16I,
    RGBA16UI,
    RGBA32F,
    RGBA32I,
    RGBA32UI,

    RGB10_A2,
    RGB10_A2UI,
    R11F_G11F_B10F,
}

impl From<ImageInternalFormat> for gl::types::GLenum {
    fn from(internal_format: ImageInternalFormat) -> gl::types::GLenum {
        use self::ImageInternalFormat::*;
        match internal_format {
            R8 => gl::R8, 
            R8SN => gl::R8_SNORM, 
            R8I => gl::R8I,
            R8UI => gl::R8UI,
            R16 => gl::R16, 
            R16F => gl::R16F,
            R16SN => gl::R16_SNORM, 
            R16I => gl::R16I,
            R16UI => gl::R16UI,
            R32F => gl::R32F,
            R32I => gl::R32I,
            R32UI => gl::R32UI,

            RG8 => gl::RG8, 
            RG8SN => gl::RG8_SNORM, 
            RG8I => gl::RG8I,
            RG8UI => gl::RG8UI,
            RG16 => gl::RG16,
            RG16F => gl::RG16F,
            RG16SN => gl::RG16_SNORM, 
            RG16I => gl::RG16I,
            RG16UI => gl::RG16UI,
            RG32F => gl::RG32F,
            RG32I => gl::RG32I,
            RG32UI => gl::RG32UI,

            RGBA8 => gl::RGBA8,
            RGBA8SN => gl::RGBA8_SNORM, 
            RGBA8I => gl::RGBA8I,
            RGBA8UI => gl::RGBA8UI,
            RGBA16 => gl::RGBA16,
            RGBA16F => gl::RGBA16F,
            RGBA16SN => gl::RGBA16_SNORM, 
            RGBA16I => gl::RGBA16I,
            RGBA16UI => gl::RGBA16UI,
            RGBA32F => gl::RGBA32F,
            RGBA32I => gl::RGBA32I,
            RGBA32UI => gl::RGBA32UI,

            RGB10_A2 => gl::RGB10_A2,
            RGB10_A2UI => gl::RGB10_A2UI,
            R11F_G11F_B10F => gl::R11F_G11F_B10F,
        }
    }
}

pub unsafe fn get_image_handle(
    id: TextureId,
    level: usize,
    layer: Option<usize>,
    format: ImageInternalFormat,
) -> Result<ImageBindlessId> {
    let id = match layer {
        Some(layer) => {
            gl::GetImageHandleARB(
                id.into(),
                level as i32,
                false as u8,
                layer as i32,
                format.into(),
            )
        }
        None => gl::GetImageHandleARB(id.into(), level as i32, true as u8, 0, format.into()),
    };

    get_error(ImageBindlessId(id))
}

pub unsafe fn make_image_handle_resident(id: ImageBindlessId, access: ImageAccess) -> Result<()> {
    gl::MakeImageHandleResidentARB(id.into(), access.into());

    get_error(())
}

pub unsafe fn make_image_handle_non_resident(id: ImageBindlessId) -> Result<()> {
    gl::MakeImageHandleNonResidentARB(id.into());

    get_error(())
}
