use std::fmt::Display;

use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};

pub mod simulation;
pub mod wire;

#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
pub struct Coord {
    pub x: u8,
    pub y: u8,
}

impl Coord {
    #[must_use]
    pub fn apply(self, direction: Direction) -> Option<Self> {
        match direction {
            Direction::Up => self.y.checked_add(1).map(|y| Self { y, x: self.x }),
            Direction::Down => self.y.checked_sub(1).map(|y| Self { y, x: self.x }),
            Direction::Left => self.x.checked_sub(1).map(|x| Self { x, y: self.y }),
            Direction::Right => self.x.checked_add(1).map(|x| Self { x, y: self.y }),
        }
    }

    #[must_use]
    pub const fn wrapping_apply(mut self, direction: Direction) -> Self {
        match direction {
            Direction::Up => self.y = self.y.wrapping_add(1),
            Direction::Down => self.y = self.y.wrapping_sub(1),
            Direction::Left => self.x = self.x.wrapping_sub(1),
            Direction::Right => self.x = self.x.wrapping_add(1),
        }
        self
    }
}

impl Display for Coord {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Sequence)]
#[serde(rename_all = "lowercase")]
pub enum Direction {
    /// Move in positive y direction
    Up,
    /// Move in negative y direction
    Down,
    /// Move in negative x direction
    Left,
    /// Move in positive x direction
    Right,
}

impl From<Direction> for usize {
    fn from(value: Direction) -> Self {
        match value {
            Direction::Up => 0,
            Direction::Down => 1,
            Direction::Left => 2,
            Direction::Right => 3,
        }
    }
}
