mod detect_support;
mod image_renderer;

pub use {
    image_renderer::*,
};

use {
    crate::{
        app::AppContext,
        display::W,
        errors::ProgramError,
        image::SourceImage,
    },
    crokey::crossterm::style::Color,
    once_cell::sync::Lazy,
    std::{
        io::Write,
        path::Path,
        sync::Mutex,
    },
    termimad::Area,
};

pub type KittyImageId = usize;

static MANAGER: Lazy<Mutex<KittyManager>> = Lazy::new(|| {
    let manager = KittyManager {
        rendered_images: Vec::new(),
        renderer: MaybeRenderer::Untested,
    };
    Mutex::new(manager)
});

pub fn manager() -> &'static Mutex<KittyManager> {
    &MANAGER
}

#[derive(Debug)]
pub struct KittyManager {
    rendered_images: Vec<RenderedImage>,
    renderer: MaybeRenderer,
}

#[derive(Debug)]
struct RenderedImage {
    image_id: KittyImageId,
    drawing_count: usize,
}

#[derive(Debug)]
enum MaybeRenderer {
    Untested,
    Disabled,
    Enabled {
        renderer: KittyImageRenderer,
    },
}

impl KittyManager {
    /// return the renderer if it's already checked and enabled, none if
    /// it's disabled or if it hasn't been tested yet
    pub fn renderer_if_tested(&mut self) -> Option<&mut KittyImageRenderer> {
        match &mut self.renderer {
            MaybeRenderer::Enabled { renderer } => Some(renderer),
            _ => None,
        }
    }
    pub fn delete_temp_files(&mut self) {
        if let MaybeRenderer::Enabled { renderer } = &mut self.renderer {
            renderer.delete_temp_files();
        }
    }
    pub fn renderer(
        &mut self,
        con: &AppContext,
    ) -> Option<&mut KittyImageRenderer> {
        if matches!(self.renderer, MaybeRenderer::Disabled) {
            return None;
        }
        if matches!(self.renderer, MaybeRenderer::Enabled { .. }) {
            return self.renderer_if_tested();
        }
        let options = KittyImageRendererOptions {
            transmission_medium: con.kitty_graphics_transmission,
            kept_temp_files: con.kept_kitty_temp_files,
        };
        match KittyImageRenderer::new(options) {
            Some(renderer) => {
                self.renderer = MaybeRenderer::Enabled { renderer };
                self.renderer_if_tested()
            }
            None => {
                self.renderer = MaybeRenderer::Disabled;
                None
            }
        }
    }
    pub fn keep(
        &mut self,
        kept_id: KittyImageId,
        drawing_count: usize,
    ) {
        for image in &mut self.rendered_images {
            if image.image_id == kept_id {
                image.drawing_count = drawing_count;
            }
        }
    }
    #[allow(clippy::too_many_arguments)] // yes, I know
    pub fn try_print_image(
        &mut self,
        w: &mut W,
        src: &SourceImage,
        src_path: &Path, // used to build a cache key
        area: &Area,
        bg: Color,
        drawing_count: usize,
        con: &AppContext,
    ) -> Result<Option<KittyImageId>, ProgramError> {
        if let Some(renderer) = self.renderer(con) {
            let img = src.optimal()?;
            let new_id = renderer.print(w, &img, src_path, area, bg)?;
            self.rendered_images.push(RenderedImage {
                image_id: new_id,
                drawing_count,
            });
            Ok(Some(new_id))
        } else {
            Ok(None)
        }
    }
    pub fn erase_images_before(
        &mut self,
        w: &mut W,
        drawing_count: usize,
    ) -> Result<(), ProgramError> {
        let mut kept_images = Vec::new();
        for image in self.rendered_images.drain(..) {
            if image.drawing_count >= drawing_count {
                kept_images.push(image);
            } else {
                let id = image.image_id;
                debug!("erase kitty image {}", id);
                write!(w, "\u{1b}_Ga=d,d=I,i={id}\u{1b}\\")?;
            }
        }
        self.rendered_images = kept_images;
        Ok(())
    }
}
