1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
mod detect_support;
mod image_renderer;

pub use {
    image_renderer::*,
};

use {
    crate::{
        display::W,
        errors::ProgramError,
        image::SourceImage,
    },
    crokey::crossterm::style::Color,
    once_cell::sync::Lazy,
    std::{
        io::Write,
        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 renderer(&mut self) -> Option<&mut KittyImageRenderer> {
        if matches!(self.renderer, MaybeRenderer::Disabled) {
            return None;
        }
        if matches!(self.renderer, MaybeRenderer::Enabled { .. }) {
            return self.renderer_if_tested();
        }
        // we're in the Untested branch
        match KittyImageRenderer::new() {
            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 self.rendered_images.iter_mut() {
            if image.image_id == kept_id {
                image.drawing_count = drawing_count;
            }
        }
    }
    pub fn try_print_image(
        &mut self,
        w: &mut W,
        src: &SourceImage,
        area: &Area,
        bg: Color,
        drawing_count: usize,
    ) -> Result<Option<KittyImageId>, ProgramError> {
        if let Some(renderer) = self.renderer() {
            let img = src.optimal()?;
            let new_id = renderer.print(w, &img, 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(())
    }
}