use std::convert::TryFrom;
use std::str::FromStr;
use anyhow::Result;

struct Plane {
    cols: usize,
    rows: usize,
}
const my_plane: Plane = Plane { cols: 8, rows: 128};

#[derive(Debug, thiserror::Error, PartialEq)]
enum ParseError {
    #[error("Expected {0}")]
    Expected(&'static str),
}

#[derive(Debug, PartialEq)]
struct Seat {
    col: usize,
    row: usize,
}

impl FromStr for Seat {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() != 10 { return Err(ParseError::Expected("10-char string")) }

        // split_at not supposed to fail as we are only working with ASCII chars here
        let s_bit = s
            .replace("F", "0")
            .replace("B", "1")
            .replace("R","1")
            .replace("L", "0")
            .chars()
            .collect::<String>();
        let (rowcode, colcode) = s_bit.split_at(7);
        Ok(Self {
            row: usize::from_str_radix(&rowcode, 2).unwrap(),
            col: usize::from_str_radix(&colcode, 2).unwrap(),
        })
    }
}

impl Seat {
    fn seat_id(&self) -> usize {
        8 * self.row + self.col
    }
}

fn main() {
    let input = include_str!("../data/input.txt");
    let mut seat_ids: Vec<usize> = input.lines().map(|l| Seat::from_str(l).unwrap().seat_id()).collect();
    seat_ids.sort();
    println!("Max seat id: {:?}", seat_ids.iter().max());
    let banana = seat_ids
        .windows(2)
        .flat_map(<&[usize;2]>::try_from) // Because windows() makes no promise on slice size 
        .find(|[a, b]| *b == *a + 2);
    eprintln!("banana = {:#?}", banana);
}

#[cfg(test)]
mod tests {
    use super::*;
    const AOC_INPUT: &str = "FBFBBFFRLR";
    #[test]
    fn test_build_seat() {
        assert_eq!(
            Seat::from_str(AOC_INPUT),
            Ok(Seat { row: 44, col: 5 })
        );
    }

    #[test]
    fn test_seat_id() {
        assert_eq!(
            Seat::from_str(AOC_INPUT).unwrap().seat_id(),
            357
        );
    }
}