use crate::{arcis_type, DataSize, EvalValue, Number, PackLocation};
use arcis_compiler::utils::{field::BaseField, used_field::UsedField};
use arcis_interpreter_proc_macros::ArcisType;
use ff::Field;
use num_traits::ToPrimitive;
use paste::paste;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};

macro_rules! impl_from_inner {
    ($t:ty) => {
        paste! {
            #[doc = "Converts"]
            #[doc = concat!("`", stringify!($t), "`")]
            #[doc = "to `Self`."]
            pub fn [<from_$t>](x: $t) -> Self {
                Self(Number::from(x).into())
            }
            #[doc = "Converts `Self` to "]
            #[doc = concat!("`", stringify!($t), "`.")]
            #[doc = "Is UB if the number is not within"]
            #[doc = concat!("`", stringify!($t), "`'s")]
            #[doc = "range."]
            pub fn [<to_$t _unchecked>](self) -> $t {
                self.0.to_signed_number().[<to_$t>]().expect("Conversion failed")
            }
        }
    };
}
macro_rules! impl_from {
    ($($t:ty),*) => {
        $(impl_from_inner!($t);)*
    };
}
/// Represents integers modulo 2^255 - 19.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ArcisType)]
pub struct BaseField25519(pub(crate) BaseField);
impl BaseField25519 {
    impl_from!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);

    /// Converts `bool` to `Self`.
    pub fn from_bool(x: bool) -> Self {
        Self(Number::from(x).into())
    }

    /// Converts `Self` to `bool`. Is UB if not 0 or 1.
    pub fn to_bool_unchecked(self) -> bool {
        self.0 == BaseField::from(true)
    }

    /// Computes 2^exponent and returns the result as a `Self`.
    pub fn power_of_two(exponent: usize) -> Self {
        Self(<BaseField as UsedField>::power_of_two(exponent))
    }

    /// Converts to little endian bytes. Returns an array.
    pub fn to_le_bytes(&self) -> [u8; 32] {
        self.0.to_le_bytes()
    }

    /// Gives the inverse. Inverse of 0 is 0.
    pub fn safe_inverse(self) -> Self {
        Self(self.0.invert().unwrap_or(BaseField::ZERO))
    }

    /// Performs field division. Division by 0 will return 0.
    pub fn field_division(self, divisor: Self) -> Self {
        self * divisor.safe_inverse()
    }
    /// Performs signed Euclidean division.
    ///
    /// Euclidean division by 0 is undefined behavior.
    pub fn euclidean_division(self, divisor: Self) -> Self {
        if divisor.0 == BaseField::ZERO {
            panic!("Division by zero");
        }
        Self(self.0.signed_euclidean_division(divisor.0))
    }
}

macro_rules! impl_binary_assign_trait {
    ($t:ident, $f:ident) => {
        impl $t for BaseField25519 {
            fn $f(&mut self, rhs: Self) {
                self.0.$f(&rhs.0);
            }
        }
    };
}
macro_rules! impl_binary_self_trait {
    ($t:ident, $f:ident) => {
        impl $t for BaseField25519 {
            type Output = Self;
            fn $f(self, rhs: Self) -> Self {
                Self(self.0.$f(rhs.0))
            }
        }
    };
}

impl_binary_assign_trait!(AddAssign, add_assign);
impl_binary_assign_trait!(SubAssign, sub_assign);
impl_binary_assign_trait!(MulAssign, mul_assign);
impl_binary_self_trait!(Add, add);
impl_binary_self_trait!(Sub, sub);
impl_binary_self_trait!(Mul, mul);

impl Neg for BaseField25519 {
    type Output = Self;
    fn neg(self) -> Self {
        Self(self.0.neg())
    }
}
