// External includes.

// Standard includes.
use std::convert::From;
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};

// Internal includes.

/// Defines rotation on a cartesian plane where each rotation is constructed out of 90-degree angles.
///
/// ```
/// # use dungen_minion_geometry::*;
/// let rotation_none: CardinalRotation = CardinalRotation::None;
///
/// let rotation_right90: CardinalRotation = rotation_none + CardinalRotation::Right90;
/// assert!(rotation_right90 == CardinalRotation::Right90);
///
/// let rotation_full180: CardinalRotation = rotation_right90 + CardinalRotation::Right90;
/// assert!(rotation_full180 == CardinalRotation::Full180);
///
/// let rotation_left90: CardinalRotation = rotation_full180 + CardinalRotation::Right90;
/// assert!(rotation_left90 == CardinalRotation::Left90);
///
/// let rotation_back_to_none: CardinalRotation = rotation_left90 + CardinalRotation::Right90;
/// assert!(rotation_back_to_none== CardinalRotation::None);
/// ```
#[derive(Copy, Clone, Debug, Display, Eq, Hash, PartialEq)]
pub enum CardinalRotation {
    /// No rotation; provided for the sake of completness.
    None,
    /// Rotate 90 degrees to the right on a 2D cartesian plane.
    Right90,
    /// Complete a full 180-degree rotation on a 2D cartesian plane. This rotation is not defined as left or right.
    Full180,
    /// Rotate 90 degrees to the left on a 2D cartesian plane.
    Left90,
}

impl Add<CardinalRotation> for CardinalRotation {
    type Output = CardinalRotation;

    fn add(self, other: CardinalRotation) -> Self::Output {
        Self::from(i8::from(self) + i8::from(other))
    }
}

impl AddAssign<CardinalRotation> for CardinalRotation {
    fn add_assign(&mut self, other: CardinalRotation) {
        *self = *self + other
    }
}

impl From<i8> for CardinalRotation {
    fn from(value: i8) -> Self {
        let mut value = value % 4;
        if value < 0 {
            value = 4 - value.abs();
        }

        match value {
            0 => CardinalRotation::None,
            1 => CardinalRotation::Right90,
            2 => CardinalRotation::Full180,
            3 => CardinalRotation::Left90,
            _ => panic!("Wrapping and if-check should not allow this: {}", value),
        }
    }
}

impl From<CardinalRotation> for i8 {
    fn from(value: CardinalRotation) -> i8 {
        match value {
            CardinalRotation::None => 0,
            CardinalRotation::Right90 => 1,
            CardinalRotation::Full180 => 2,
            CardinalRotation::Left90 => 3,
        }
    }
}

impl Neg for CardinalRotation {
    type Output = CardinalRotation;

    fn neg(self) -> Self::Output {
        CardinalRotation::from(i8::from(self) + 2)
    }
}

impl Sub<CardinalRotation> for CardinalRotation {
    type Output = CardinalRotation;

    fn sub(self, other: CardinalRotation) -> Self::Output {
        Self::from(i8::from(self) - i8::from(other))
    }
}

impl SubAssign<CardinalRotation> for CardinalRotation {
    fn sub_assign(&mut self, other: CardinalRotation) {
        *self = *self - other
    }
}
