use std::{
    sync::{mpsc, Arc},
    thread,
};

use eframe::egui::{self, TextureOptions};
use image::{DynamicImage, Pixel, Rgb, Rgb32FImage};
use nalgebra::{SMatrix, Vector4};
use rayon::prelude::*;

use image::EncodableLayout;
struct ProcessorInstruction {
    img: Arc<Rgb32FImage>,
    color_matrix: SMatrix<f32, 3, 4>,
    pos_matrix: SMatrix<f32, 2, 3>,
    ret: oneshot::Sender<(egui::TextureHandle, (f32, f32))>,
    ctx: egui::Context,
    handle: Option<egui::TextureHandle>,
}

pub struct Processor {
    channel: mpsc::Sender<ProcessorInstruction>,
}

impl Processor {
    pub fn init() -> Self {
        let (tx, rx) = mpsc::channel();
        thread::spawn(|| {
            for ProcessorInstruction {
                img,
                color_matrix,
                pos_matrix,
                ret,
                ctx,
                handle,
            } in rx
            {
                let (width, height) = img.dimensions();

                let mut r = (*img).clone();
                if !color_matrix.is_identity(f32::EPSILON) {
                    r.par_pixels_mut().for_each(|px| {
                        *px = Rgb::from(
                            TryInto::<[f32; 3]>::try_into(
                                (color_matrix * Vector4::new(px.0[0], px.0[1], px.0[2], 1.))
                                    .as_slice(),
                            )
                            .unwrap(),
                        )
                    });
                }

                let color_image = {
                    let image = DynamicImage::from(r).to_rgb8();

                    egui::ColorImage::from_rgb(
                        [image.width() as usize, image.height() as usize],
                        image.as_bytes(),
                    )
                };
                let handle = if let Some(mut handle) = handle {
                    handle.set(color_image, TextureOptions::default());
                    handle
                } else {
                    ctx.load_texture("res_tex", color_image, TextureOptions::default())
                };

                ret.send((handle, (width as f32, height as f32))).unwrap();
                // (color_matrix.is_identity(f32::EPSILON), pos_matrix.is_identity(f32::EPSILON))
            }
        });
        Self { channel: tx }
    }

    pub fn exec(
        &self,
        img: Arc<Rgb32FImage>,
        color_matrix: SMatrix<f32, 3, 4>,
        pos_matrix: SMatrix<f32, 2, 3>,
        ctx: egui::Context,
        handle: Option<egui::TextureHandle>,
    ) -> oneshot::Receiver<(egui::TextureHandle, (f32, f32))> {
        let (tx, rx) = oneshot::channel();

        let r = self.channel.send(ProcessorInstruction {
            img,
            color_matrix,
            pos_matrix,
            ret: tx,
            ctx,
            handle,
        });
        dbg!(r).unwrap();

        rx
    }
}