use std::fmt::Display;

use aoc::termion::{color, style};
use parse_display::FromStr;

#[derive(Debug, Clone, Copy, FromStr)]
#[display("move {number} from {from} to {to}")]
pub struct Instruction {
    pub number: usize,
    pub from: usize,
    pub to: usize,
}

pub struct Crates {
    pub crates: Vec<Vec<char>>,
}

impl std::str::FromStr for Crates {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s = s
            .lines()
            .rev()
            .skip(1)
            .map(|s| s.chars().collect::<Vec<char>>())
            .collect::<Vec<_>>();

        let mut crates = Vec::new();

        for line in s.iter() {
            for (i, c) in line.iter().skip(1).step_by(4).enumerate() {
                if crates.len() <= i {
                    crates.push(Vec::new());
                }

                if c.is_alphabetic() {
                    crates[i].push(*c);
                }
            }
        }

        Ok(Crates { crates })
    }
}

impl Display for Crates {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let longest_stack = self.crates.iter().map(|s| s.len()).max().unwrap();

        let mut highest_stack = vec![true; self.crates.len()];

        for i in (0..longest_stack).rev() {
            for (si, stack) in self.crates.iter().enumerate() {
                if let Some(c) = stack.get(i) {
                    if highest_stack[si] {
                        highest_stack[si] = false;

                        write!(
                            f,
                            " [{}{}{}{}{}{}] ",
                            style::Bold,
                            style::Blink,
                            color::Fg(color::Yellow),
                            c,
                            style::Reset,
                            color::Fg(color::LightWhite)
                        )?;
                    } else {
                        write!(f, " [{c}] ")?;
                    }
                } else {
                    write!(f, "     ")?;
                }
            }
            writeln!(f)?;
        }

        for i in 1..self.crates.len() + 1 {
            write!(f, "  {i}  ")?;
        }
        writeln!(f)?;

        Ok(())
    }
}