pub mod bayer;
pub mod crop;
pub mod passthrough;
pub mod yuv;
use glium::framebuffer::ValidationError;
use glium::texture::UnsignedTexture2d;
use thiserror::Error;
use crate::egl::Dimensions;
use crate::egl::Texture;
use crate::import_bind_texture;
use crate::uniform_extend;
use crate::uniforms::ExtendUniforms;
use glium::{self, IndexBuffer, VertexBuffer, Program, Surface};
use glium::uniforms::Uniforms;
use std::error;
use std::marker::PhantomData;
#[derive(Error, Debug)]
enum Error {
    #[error("Transforming source texture doesn't create an image matching target dimensions: {msg}. Expected {expected:?}, got {got:?}")]
    BadDimensions {
        expected: Dimensions, 
        got: Dimensions,
        msg: &'static str,
    },
    #[error("Source is not in a supported format")]
    BadFormat(&'static str),
}
#[derive(Copy, Clone, Debug)]
struct Vertex {
    position: [f32; 2],
    tex_coords: [f32; 2],
}
glium::implement_vertex!(Vertex, position, tex_coords);
fn covering_vertices<F: glium::backend::Facade>(facade: &F, (width, height): (u32, u32))
    -> Result<(VertexBuffer<Vertex>, IndexBuffer<u16>), Box<dyn error::Error>>
{
    Ok((
        VertexBuffer::new(
            facade,
            &[
                Vertex { position: [-1.0, -3.0], tex_coords: [0.0, height as f32 * 2.] },
                Vertex { position: [-1.0,  1.0], tex_coords: [0.0, 0.0] },
                Vertex { position: [3.0,  1.0], tex_coords: [width as f32 * 2., 0.0] },
            ]
        )?,
        IndexBuffer::new(
            facade,
            glium::index::PrimitiveType::TriangleStrip,
            &[1 as u16, 2, 0],
        )?,
     ))
}
pub struct Shader<T>{
    program: Program,
    vertices: glium::VertexBuffer<Vertex>,
    indices: glium::IndexBuffer<u16>,
    _data: PhantomData<T>,
}
pub fn import_target_texture(
    facade: &impl glium::backend::Facade,
    target_tex: &Texture,
) -> UnsignedTexture2d {
    let Dimensions { width, height } = target_tex.dimensions();
    unsafe {
        use glium::texture::*;
        UnsignedTexture2d::from_id(
            facade,
            UncompressedUintFormat::U8U8U8U8,
            target_tex.id().as_gl_id(),
            false,
            MipmapsOption::NoMipmap,
            Dimensions::Texture2d { width, height },
        )
    }
}
pub fn texture_to_surface<'a>(facade: &impl glium::backend::Facade, target: &'a mut UnsignedTexture2d)
    -> Result<glium::framebuffer::SimpleFrameBuffer<'a>, ValidationError>
{
        glium::framebuffer::SimpleFrameBuffer::new(facade, &*target)
}
impl<W> Shader<W> {    
    fn draw_any<'a, F: glium::backend::Facade>(
        &self,
        facade: &F,
        source_tex: Texture,
        target: &mut impl Surface,
        uniforms: impl Uniforms + ExtendUniforms<'a>,
    ) -> Result<(), Box<dyn error::Error>>{
        let Dimensions { width: source_width, height: source_height } = source_tex.dimensions();
        let (source, tex_binding) = import_bind_texture(facade, source_tex)?;
        let uniforms = uniform_extend! {
            uniforms,
            frameno: 0,
            source_dims: (
                source_width as f32, source_height as f32,
                1.0 / source_width as f32, 1.0 / source_height as f32,
            ),
            source: source
                .sampled()
                .wrap_function(glium::uniforms::SamplerWrapFunction::Repeat)
                .minify_filter(glium::uniforms::MinifySamplerFilter::Nearest)
                .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
        };
        
        target.clear_color(0.0, 0.0, 1.0, 1.0);
        target.draw(
            &self.vertices,
            &self.indices,
            &self.program,
            &uniforms,
            &Default::default(),
        )?;
        drop(tex_binding);
        
        facade.get_context().flush();
        facade.get_context().finish();
        Ok(())
    }
}