use std::{fmt::Display, ops::{Add, Neg, AddAssign, Sub, SubAssign}};

use crate::MAX_DEPTH;

#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
pub struct Score(i16);

impl Score {
    pub(crate) const CHECKMATE: Score = Score(10000);
    pub(crate) const DRAW: Score = Score(0);
    pub(crate) const ZERO: Score = Score(0);
    pub(crate) const BLANK: Score = Score(0);
    pub(crate) const STALEMATE: Score = Score(0);
    pub(crate) const REPETITION: Score = Score(0);
    pub(crate) const START_ALPHA: Score = Score(-32001);
    pub(crate) const START_BETA: Score = Score(32001);
    
    #[inline(always)]
    pub fn abs(self) -> Score {
        Score::from(self.0.abs())
    }

    #[inline(always)]
    pub fn signum(self) -> Score {
        Score::from(self.0.signum())
    }

    #[inline(always)]
    pub(crate) fn checkmate_minus_depth(depth: usize) -> Score {
        Score::from(Self::CHECKMATE.0 - depth as i16)
    }
    
    #[inline(always)]
    pub fn is_checkmate(self) -> bool {
        self.abs() >= Score::checkmate_minus_depth(MAX_DEPTH)
    }

    #[inline(always)]
    pub fn is_positive(self) -> bool {
        self.0 > 0
    }

    #[inline(always)]
    pub fn is_negative(self) -> bool {
        self.0 < 0
    }
}

impl From<i16> for Score {
    fn from(value: i16) -> Self {
        unsafe { std::mem::transmute(value) }
    }
}

impl From<Score> for f32 {
    fn from(score: Score) -> Self {
        score.0 as f32
    }
}

impl From<Score> for i16 {
    fn from(score: Score) -> Self {
        score.0
    }
}

impl Neg for Score {
    type Output = Self;

    fn neg(self) -> Self::Output {
        Self::from(-self.0)
    }
}

impl Add for Score {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        Score::from(self.0 + other.0)
    }
}

impl AddAssign for Score {
    fn add_assign(&mut self, other: Self) {
        self.0 += other.0;
    }
}

impl Sub for Score {
    type Output = Self;
    fn sub(self, other: Self) -> Self {
        Score::from(self.0 - other.0)
    }
}

impl SubAssign for Score {
    fn sub_assign(&mut self, other: Self) {
        self.0 -= other.0;
    }
}

impl Add<i16> for Score {
    type Output = Self;
    fn add(self, rhs: i16) -> Self::Output {
        Score::from(self.0 + rhs)
    }
}

impl AddAssign<i16> for Score {
    fn add_assign(&mut self, other: i16) {
        self.0 += other;
    }
}

impl Sub<i16> for Score {
    type Output = Self;
    fn sub(self, rhs: i16) -> Self::Output {
        Score::from(self.0 - rhs)
    }
}

impl SubAssign<i16> for Score {
    fn sub_assign(&mut self, other: i16) {
        self.0 -= other;
    }
}

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