use super::{utils::twos_complement, BigIntConversionError, ParseSignedError, Sign, Signed};
use alloc::string::String;
use core::str::FromStr;
use ruint::Uint;
impl<const BITS: usize, const LIMBS: usize> TryFrom<Uint<BITS, LIMBS>> for Signed<BITS, LIMBS> {
    type Error = BigIntConversionError;
    #[inline]
    fn try_from(from: Uint<BITS, LIMBS>) -> Result<Self, Self::Error> {
        let value = Self(from);
        match value.sign() {
            Sign::Positive => Ok(value),
            Sign::Negative => Err(BigIntConversionError),
        }
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for Uint<BITS, LIMBS> {
    type Error = BigIntConversionError;
    #[inline]
    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
        match value.sign() {
            Sign::Positive => Ok(value.0),
            Sign::Negative => Err(BigIntConversionError),
        }
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<&str> for Signed<BITS, LIMBS> {
    type Error = ParseSignedError;
    #[inline]
    fn try_from(value: &str) -> Result<Self, Self::Error> {
        Self::from_str(value)
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<&String> for Signed<BITS, LIMBS> {
    type Error = ParseSignedError;
    #[inline]
    fn try_from(value: &String) -> Result<Self, Self::Error> {
        value.parse()
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<String> for Signed<BITS, LIMBS> {
    type Error = ParseSignedError;
    #[inline]
    fn try_from(value: String) -> Result<Self, Self::Error> {
        value.parse()
    }
}
impl<const BITS: usize, const LIMBS: usize> FromStr for Signed<BITS, LIMBS> {
    type Err = ParseSignedError;
    #[inline]
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (sign, s) = match s.as_bytes().first() {
            Some(b'+') => (Sign::Positive, &s[1..]),
            Some(b'-') => (Sign::Negative, &s[1..]),
            _ => (Sign::Positive, s),
        };
        let abs = Uint::<BITS, LIMBS>::from_str(s)?;
        Self::checked_from_sign_and_abs(sign, abs).ok_or(ParseSignedError::IntegerOverflow)
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for i128 {
    type Error = BigIntConversionError;
    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
        if value.bits() > 128 {
            return Err(BigIntConversionError);
        }
        if value.is_positive() {
            Ok(u128::try_from(value.0).unwrap() as Self)
        } else {
            let u = twos_complement(value.0);
            let u = u128::try_from(u).unwrap() as Self;
            Ok((!u).wrapping_add(1))
        }
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<i128> for Signed<BITS, LIMBS> {
    type Error = BigIntConversionError;
    fn try_from(value: i128) -> Result<Self, Self::Error> {
        let u = value as u128;
        if value >= 0 {
            return Self::try_from(u);
        }
        let tc = (!u).wrapping_add(1);
        let stc = Uint::<128, 2>::saturating_from(tc);
        let (num, overflow) = Uint::<BITS, LIMBS>::overflowing_from_limbs_slice(stc.as_limbs());
        if overflow {
            return Err(BigIntConversionError);
        }
        Ok(Self(twos_complement(num)))
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for u128 {
    type Error = BigIntConversionError;
    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
        if value.is_negative() {
            return Err(BigIntConversionError);
        }
        let saturated = Uint::<BITS, LIMBS>::saturating_from(Self::MAX);
        if value > Signed(saturated) {
            return Err(BigIntConversionError);
        }
        value.into_raw().try_into().map_err(|_| BigIntConversionError)
    }
}
impl<const BITS: usize, const LIMBS: usize> TryFrom<u128> for Signed<BITS, LIMBS> {
    type Error = BigIntConversionError;
    fn try_from(value: u128) -> Result<Self, Self::Error> {
        let saturated = Uint::<BITS, LIMBS>::saturating_from(value);
        if value != saturated.to::<u128>() {
            return Err(BigIntConversionError);
        }
        Self::try_from(saturated)
    }
}
macro_rules! impl_conversions {
    ($(
        $u:ty [$actual_low_u:ident -> $low_u:ident, $as_u:ident],
        $i:ty [$actual_low_i:ident -> $low_i:ident, $as_i:ident];
    )+) => {
        impl<const BITS: usize, const LIMBS: usize> Signed<BITS, LIMBS> {
            $(
                impl_conversions!(@impl_fns $u, $actual_low_u $low_u $as_u);
                impl_conversions!(@impl_fns $i, $actual_low_i $low_i $as_i);
            )+
        }
        $(
            impl<const BITS: usize, const LIMBS: usize> TryFrom<$u> for Signed<BITS, LIMBS> {
                type Error = BigIntConversionError;
                #[inline]
                fn try_from(value: $u) -> Result<Self, Self::Error> {
                    let u = Uint::<BITS, LIMBS>::try_from(value).map_err(|_| BigIntConversionError)?;
                    Signed::checked_from_sign_and_abs(Sign::Positive, u).ok_or(BigIntConversionError)
                }
            }
            impl<const BITS: usize, const LIMBS: usize> TryFrom<$i> for Signed<BITS, LIMBS> {
                type Error = BigIntConversionError;
                #[inline]
                fn try_from(value: $i) -> Result<Self, Self::Error> {
                    let uint: $u = value as $u;
                    if value.is_positive() {
                        return Self::try_from(uint);
                    }
                    let abs = (!uint).wrapping_add(1);
                    let tc = twos_complement(Uint::<BITS, LIMBS>::from(abs));
                    Ok(Self(tc))
                }
            }
            impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for $u {
                type Error = BigIntConversionError;
                #[inline]
                fn try_from(value: Signed<BITS, LIMBS>) -> Result<$u, Self::Error> {
                    u128::try_from(value)?.try_into().map_err(|_| BigIntConversionError)
                }
            }
            impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for $i {
                type Error = BigIntConversionError;
                #[inline]
                fn try_from(value: Signed<BITS, LIMBS>) -> Result<$i, Self::Error> {
                    i128::try_from(value)?.try_into().map_err(|_| BigIntConversionError)
                }
            }
        )+
    };
    (@impl_fns $t:ty, $actual_low:ident $low:ident $as:ident) => {
        #[inline]
        pub const fn $low(&self) -> $t {
            if BITS == 0 {
                return 0
            }
            self.0.as_limbs()[0] as $t
        }
        #[doc = concat!("Conversion to ", stringify!($t) ," with overflow checking.")]
        #[doc = concat!("Panics if the number is outside the ", stringify!($t), " valid range.")]
        #[inline]
        #[track_caller]
        pub fn $as(&self) -> $t {
            <$t as TryFrom<Self>>::try_from(*self).unwrap()
        }
    };
}
impl_conversions! {
    u8   [low_u64  -> low_u8,    as_u8],    i8   [low_u64  -> low_i8,    as_i8];
    u16  [low_u64  -> low_u16,   as_u16],   i16  [low_u64  -> low_i16,   as_i16];
    u32  [low_u64  -> low_u32,   as_u32],   i32  [low_u64  -> low_i32,   as_i32];
    u64  [low_u64  -> low_u64,   as_u64],   i64  [low_u64  -> low_i64,   as_i64];
    usize[low_u64  -> low_usize, as_usize], isize[low_u64  -> low_isize, as_isize];
}