use glow::HasContext;

use crate::components::{CodeType, Program, Snippet};
use crate::errors::*;

use super::ColorMap;

pub struct LinearColormap {
    snippet: Snippet,
    colors: Vec<[u8; 4]>,

    color_changed: bool,
    texture: Option<glow::NativeTexture>,

    pub min: f32,
    pub max: f32,
}

impl LinearColormap {
    pub fn new() -> Result<Self> {
        let snippet = Snippet::new(
            "linear",
            CodeType::<&'static str>::Path("colormap.glsl".into()),
            true,
            None,
        )?;

        Ok(Self {
            snippet,
            colors: vec![],
            texture: None,
            color_changed: true,
            min: 0.0,
            max: 1.0,
        })
    }

    pub fn set_colors(&mut self, colors: Vec<[u8; 4]>) {
        self.colors = colors;
        self.color_changed = true;
    }

    pub fn set_range(&mut self, min: f32, max: f32) {
        self.min = min;
        self.max = max;
    }
}

impl ColorMap for LinearColormap {
    fn attach_with_program(
        &mut self,
        gl: &glow::Context,
        program: &crate::components::Program,
    ) -> crate::errors::Result<()> {
        use bytemuck::cast_slice;
        unsafe {
            if self.color_changed {
                let texture = gl.create_texture().unwrap();
                gl.bind_texture(glow::TEXTURE_1D, Some(texture));
                gl.tex_image_1d(
                    glow::TEXTURE_1D,
                    0,
                    glow::RGBA as i32,
                    self.colors.len() as i32,
                    0,
                    glow::RGBA,
                    glow::UNSIGNED_BYTE,
                    Some(cast_slice(&self.colors)),
                );

                gl.tex_parameter_i32(
                    glow::TEXTURE_1D,
                    glow::TEXTURE_MIN_FILTER,
                    glow::NEAREST as i32,
                );
                gl.tex_parameter_i32(
                    glow::TEXTURE_1D,
                    glow::TEXTURE_MAG_FILTER,
                    glow::NEAREST as i32,
                );

                let name = self.snippet.find_symbol("conf").unwrap();
                let location = program.get_uniform_location(gl, &name);

                // gl.uniform_2_f32(location.as_ref(), 0);
                gl.uniform_4_f32(
                    location.as_ref(),
                    self.min,
                    self.max,
                    self.colors.len() as f32,
                    125.0,
                );

                self.texture = Some(texture);
            }
        }

        Ok(())
    }

    fn destroy(&mut self, gl: &glow::Context) {
        unsafe {
            if let Some(texture) = self.texture {
                gl.delete_texture(texture);
            }
        }
    }

    fn bind_texture(&self, gl: &glow::Context, program: &Program) -> crate::errors::Result<()> {
        unsafe {
            gl.active_texture(glow::TEXTURE0);
            gl.bind_texture(glow::TEXTURE_1D, self.texture);
            let name = self.snippet.find_symbol("colormap").unwrap();
            let location = program.get_uniform_location(gl, &name);
            gl.uniform_1_i32(location.as_ref(), 0);
        }

        Ok(())
    }

    fn snippet_ref(&self) -> &crate::components::Snippet {
        &self.snippet
    }
}

mod test {
    #[test]

    fn test() {
        use super::LinearColormap;

        let mut colormap = LinearColormap::new().unwrap();

        // colormap.set_colors(vec![[0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0, 1.0]]);
        colormap.set_range(0.0, 1.0);
    }
}
