use std::fmt::Write;

const WIDTH: usize = 17;
const HEIGHT: usize = 9;
const TILES: &[char] = &[
    ' ', '.', 'o', '+', '=', '*', 'B', 'O', 'X', '@', '%', '&', '#', '/', '^',
];

pub struct RandomArt {
    tiles: [[i8; WIDTH]; HEIGHT],
}

impl RandomArt {
    pub fn render(&self, title: &str, subtitle: &str) -> Result<String, std::fmt::Error> {
        let mut string = String::new();

        string.write_str(&format!("+{:-^17}+\n", format!("[{}]", title)))?;

        for row in self.tiles {
            string.write_char('|')?;
            for cell in row {
                if cell == -1 {
                    string.write_char('S')?;
                } else if cell == -2 {
                    string.write_char('E')?;
                } else {
                    let cell = (TILES.len() - 1).min(cell as usize);
                    string.write_char(TILES[cell])?;
                }
            }
            string.write_char('|')?;
            string.write_char('\n')?;
        }

        string.write_str(&format!("+{:-^17}+\n", format!("[{}]", subtitle)))?;

        Ok(string)
    }

    pub fn from_digest(digest: &[u8]) -> Self {
        let mut tiles = [[0i8; WIDTH]; HEIGHT];
        let mut x = WIDTH / 2;
        let mut y = HEIGHT / 2;

        tiles[y][x] = -1;

        for byte in digest.iter() {
            for i in 0..4 {
                let b = (*byte >> (i * 2)) & 3;
                match b {
                    0 | 1 => {
                        if y > 0 {
                            y -= 1;
                        }
                    }
                    2 | 3 => {
                        if y < HEIGHT - 1 {
                            y += 1;
                        }
                    }
                    _ => {}
                }
                match b {
                    0 | 2 => {
                        if x > 0 {
                            x -= 1;
                        }
                    }
                    1 | 3 => {
                        if x < WIDTH - 1 {
                            x += 1;
                        }
                    }
                    _ => {}
                }

                if tiles[y][x] >= 0 {
                    tiles[y][x] += 1;
                }
            }
        }

        tiles[y][x] = -2;

        Self { tiles }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const FINGERPRINT: &str = "L5N7A2PETaGegW5P3qh/Vjd8AW6Mn4B+VB2SHK+eZCY=";

    #[test]
    fn render() {
        let randomart = RandomArt::from_digest(&base64::decode(FINGERPRINT).unwrap())
            .render("Title", "Subtitle")
            .unwrap();

        println!("{}", randomart);
    }
}
