use std::convert::{From, TryFrom};

use traiter::numbers::{Endianness, Signed, ToBytes, Zeroable};

use crate::traits::Oppose;

use super::constants::MIDDLE_BYTE;
use super::digits::{negate_bytes, BinaryBaseFromBinaryDigits};
use super::types::BigInt;

impl<
        Digit: BinaryBaseFromBinaryDigits<Digit> + Copy + From<u8> + Oppose,
        const DIGIT_BITNESS: usize,
    > ToBytes for &BigInt<Digit, DIGIT_BITNESS>
where
    for<'a> &'a Digit: Zeroable,
    u8: TryFrom<Digit>,
{
    type Output = Vec<u8>;

    fn to_bytes(self, endianness: Endianness) -> Self::Output {
        let mut result = Digit::binary_base_from_binary_digits(
            &self.digits,
            DIGIT_BITNESS,
            u8::BITS as usize,
        )
        .iter()
        .map(|&byte| unsafe { u8::try_from(byte).unwrap_unchecked() })
        .collect::<Vec<u8>>();
        let most_significant_byte = result[result.len() - 1];
        if most_significant_byte >= MIDDLE_BYTE
            && !(most_significant_byte == MIDDLE_BYTE
                && result.iter().rev().skip(1).all(Zeroable::is_zero)
                && self.is_negative())
        {
            result.push(0u8);
        }
        if self.is_negative() {
            negate_bytes(&mut result);
        }
        match endianness {
            Endianness::Big => result.reverse(),
            Endianness::Little => {}
        }
        result
    }
}
