use crate::*;

use std::{
    fmt::{self, Display, Formatter},
    ops::{Div, DivAssign, Mul, MulAssign},
    str::FromStr,
};

use derive_more::{Add, AddAssign, Neg, Sub, SubAssign};
use inner_space::{DotProduct, InnerSpace, VectorSpace};
use scalars::{Exp, Real, Zero};

/// The 3D bivector type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Add, AddAssign, Sub, SubAssign, Neg)]
pub struct Bivector<T> {
    /// The component representing the xy plane.
    pub xy: T,
    /// The component representing the xz plane.
    pub xz: T,
    /// The component representing the yz plane.
    pub yz: T,
}

impl<T> Bivector<T> {
    /// Creates a new bivector from its components.
    pub fn new(xy: T, xz: T, yz: T) -> Self {
        Self { xy, xz, yz }
    }
}

impl<T: Real> Exp for Bivector<T> {
    type Output = Rotor<T>;

    fn exp(self) -> Rotor<T> {
        let magnitude = self.magnitude();
        let [sin, cos] = magnitude.sin_cos();
        let scale = if magnitude.is_zero() {
            T::zero()
        } else {
            sin / magnitude
        };

        Rotor {
            scalar: cos,
            xy: self.xy * scale,
            xz: self.xz * scale,
            yz: self.yz * scale,
        }
    }

    fn exp2(self) -> Rotor<T> {
        self.exp()
    }
}

impl<T: Zero> Bivector<T> {
    /// Creates a new bivector along the xy plane.
    pub fn xy(xy: T) -> Self {
        Self {
            xy,
            xz: T::zero(),
            yz: T::zero(),
        }
    }

    /// Creates a new bivector along the xz plane.
    pub fn xz(xz: T) -> Self {
        Self {
            xy: T::zero(),
            xz,
            yz: T::zero(),
        }
    }

    /// Creates a new bivector along the yz plane.
    pub fn yz(yz: T) -> Self {
        Self {
            xy: T::zero(),
            xz: T::zero(),
            yz,
        }
    }
}

impl<T: Zero> Zero for Bivector<T> {
    fn zero() -> Self {
        Self {
            xy: T::zero(),
            xz: T::zero(),
            yz: T::zero(),
        }
    }

    fn is_zero(&self) -> bool {
        self.xy.is_zero() && self.xz.is_zero() && self.yz.is_zero()
    }
}

impl<T> Mul<T> for Bivector<T>
where
    T: Mul<Output = T> + Copy,
{
    type Output = Self;
    fn mul(self, other: T) -> Self {
        Self {
            xy: self.xy * other,
            xz: self.xz * other,
            yz: self.yz * other,
        }
    }
}

impl<T> MulAssign<T> for Bivector<T>
where
    T: MulAssign + Copy,
{
    fn mul_assign(&mut self, other: T) {
        self.xy *= other;
        self.xz *= other;
        self.yz *= other;
    }
}

impl<T> Div<T> for Bivector<T>
where
    T: Div<Output = T> + Copy,
{
    type Output = Self;
    fn div(self, other: T) -> Self {
        Self {
            xy: self.xy / other,
            xz: self.xz / other,
            yz: self.yz / other,
        }
    }
}

impl<T> DivAssign<T> for Bivector<T>
where
    T: DivAssign + Copy,
{
    fn div_assign(&mut self, other: T) {
        self.xy /= other;
        self.xz /= other;
        self.yz /= other;
    }
}

impl<T: Real> VectorSpace for Bivector<T> {
    type Scalar = T;
}

impl<T: Real> DotProduct for Bivector<T> {
    type Output = Self::Scalar;

    fn dot(&self, other: &Self) -> T {
        -self.xy * other.xy - self.xz * other.xz - self.yz * other.yz
    }

    fn scalar(&self, other: &Self) -> T {
        self.xy * other.xy + self.xz * other.xz + self.yz * other.yz
    }
}

impl<T: Real> DotProduct<Vector<T>> for Bivector<T> {
    type Output = Vector<T>;
    #[inline]
    fn dot(&self, other: &Vector<T>) -> Vector<T> {
        other.dot(self)
    }
}

impl<T: Real> DotProduct<Rotor<T>> for Bivector<T> {
    type Output = Rotor<T>;
    fn dot(&self, other: &Rotor<T>) -> Rotor<T> {
        Rotor {
            scalar: -self.xy * other.xy - self.xz * other.xz - self.yz * other.yz,
            xy: self.xy * other.scalar + self.yz * other.xz - self.xz * other.yz,
            xz: self.xz * other.scalar - self.yz * other.xy + self.xy * other.yz,
            yz: self.yz * other.scalar + self.xz * other.xy - self.xy * other.xz,
        }
    }
}

impl<T: Display> Display for Bivector<T> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        write!(formatter, "({}, {}, {})", self.xy, self.xz, self.yz)
    }
}

impl<T: FromStr> FromStr for Bivector<T> {
    type Err = ParseBivectorError;

    fn from_str(source: &str) -> Result<Self, Self::Err> {
        let trimmed = source.trim();
        let inner = trimmed
            .strip_prefix('(')
            .and_then(|s| s.strip_suffix(')'))
            .ok_or(ParseBivectorError)?;
        let parts: Vec<&str> = inner.split(',').collect();
        if parts.len() != 3 {
            return Err(ParseBivectorError);
        }
        let xy = parts[0].trim().parse().map_err(|_| ParseBivectorError)?;
        let xz = parts[1].trim().parse().map_err(|_| ParseBivectorError)?;
        let yz = parts[2].trim().parse().map_err(|_| ParseBivectorError)?;
        Ok(Self { xy, xz, yz })
    }
}

/// Error returned when parsing a bivector from a string fails.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseBivectorError;

impl Display for ParseBivectorError {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        write!(formatter, "invalid bivector format")
    }
}

impl std::error::Error for ParseBivectorError {}
