use std::ops::{Mul, MulAssign};

use super::{Number, Sign};

impl Mul for Number {
    type Output = Self;

    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, rhs: Self) -> Self::Output {
        let sign = match (self.sign, rhs.sign) {
            (Sign::Positif, Sign::Positif) => Sign::Positif,
            (Sign::Positif, Sign::Negatif) => Sign::Negatif,
            (Sign::Negatif, Sign::Positif) => Sign::Negatif,
            (Sign::Negatif, Sign::Negatif) => Sign::Positif,
        };
        let mut digits = Vec::new();
        for (zeroes, rdigit) in rhs.digits.iter().enumerate() {
            for (idx, ldigit) in self.digits.iter().enumerate() {
                let mult = rdigit * ldigit;
                let new = mult % 10;
                let mut carry = mult / 10;
                if let Some(old) = digits.get(zeroes + idx) {
                    let add = old + new;
                    digits[zeroes + idx] = add % 10;
                    carry += add / 10;
                } else {
                    digits.push(new);
                }
                let mut carry_idx = 1;
                loop {
                    if carry == 0 {
                        break;
                    }
                    if let Some(old) = digits.get(zeroes + idx + carry_idx) {
                        let add = old + carry;
                        digits[zeroes + idx + carry_idx] = add % 10;
                        carry = add / 10;
                    } else {
                        digits.push(carry % 10);
                        carry /= 10;
                    }
                    carry_idx += 1;
                }
            }
        }
        Number { digits, sign }
    }
}

impl Mul<Number> for isize {
    type Output = Number;

    fn mul(self, rhs: Number) -> Self::Output {
        Number::from(self) * rhs
    }
}

impl Mul<isize> for Number {
    type Output = Self;

    fn mul(self, rhs: isize) -> Self::Output {
        self * Number::from(rhs)
    }
}

impl MulAssign for Number {
    fn mul_assign(&mut self, rhs: Self) {
        let new = self.clone() * rhs;
        self.digits = new.digits;
        self.sign = new.sign;
    }
}

#[cfg(test)]
mod test_number_mul {
    use crate::number::Number;

    #[test]
    fn mul_positif() {
        let a = Number::from(9);
        let b = Number::from(9);
        let res = Number::from(81);
        assert_eq!(res, a * b);
    }

    #[test]
    fn mul_negatif() {
        let a = Number::from(-9);
        let b = Number::from(-9);
        let res = Number::from(81);
        assert_eq!(res, a * b);
    }

    #[test]
    fn mul_positif_with_negatif() {
        let a = Number::from(9);
        let b = Number::from(-9);
        let res = Number::from(-81);
        assert_eq!(res, a * b);
    }

    #[test]
    fn mul_negatif_with_positif() {
        let a = Number::from(-9);
        let b = Number::from(9);
        let res = Number::from(-81);
        assert_eq!(res, a * b);
    }

    #[test]
    fn mul_eleven_x_ten() {
        let a = Number::from(11);
        let b = Number::from(10);
        let res = Number::from(110);
        assert_eq!(res, a * b);
    }

    #[test]
    fn mul_eleven_x_eleven() {
        let a = Number::from(11);
        let b = Number::from(11);
        let res = Number::from(121);
        assert_eq!(res, a * b);
    }

    #[test]
    fn mul_big_1() {
        let a = Number::from("123");
        let b = Number::from("321");
        let res = Number::from("39483");
        assert_eq!(res, a * b);
    }

    #[test]
    fn mul_big_2() {
        let a = Number::from("123456789");
        let b = Number::from("987654321");
        let res = Number::from("121932631112635269");
        assert_eq!(res, a * b);
    }
}