use traiter::numbers::{Endianness, FromBytes, One, Zeroable};

use crate::traits::HasSignBit;

use super::constants::MIDDLE_BYTE;
use super::contracts::is_valid_digit_bitness;
use super::digits::{
    negate_bytes, to_digits_sign, BinaryBaseFromBinaryDigits,
};
use super::types::{BigInt, Sign};

impl<
        Digit: BinaryBaseFromBinaryDigits<u8> + HasSignBit,
        const DIGIT_BITNESS: usize,
    > FromBytes for BigInt<Digit, DIGIT_BITNESS>
{
    fn from_bytes(bytes: &[u8], endianness: Endianness) -> Self {
        debug_assert!(is_valid_digit_bitness::<Digit, DIGIT_BITNESS>());
        let mut bytes = bytes.to_vec();
        match endianness {
            Endianness::Big => bytes.reverse(),
            Endianness::Little => {}
        }
        let most_significant_byte = bytes[bytes.len() - 1];
        let sign = if most_significant_byte >= MIDDLE_BYTE {
            negate_bytes(&mut bytes);
            -Sign::one()
        } else {
            to_digits_sign(&bytes)
        };
        Self {
            sign,
            digits: Digit::binary_base_from_binary_digits(
                &bytes[..bytes.len()
                    - usize::from(
                        bytes.len() > 1 && bytes[bytes.len() - 1].is_zero(),
                    )],
                u8::BITS as usize,
                DIGIT_BITNESS,
            ),
        }
    }
}
