//! Lowercase hexadecimal formatting via [`fmt::LowerHex`].
use super::U512;
use core::fmt;

/// Formats a [`U512`] as a lowercase hexadecimal string without leading
/// zeros (except for the value zero itself, which formats as `"0"`).
///
/// Supports the `#` alternate flag for a `0x` prefix, matching the
/// standard library convention for integer types.
///
/// # Examples
///
/// ```
/// use cnfy_uint::u512::U512;
///
/// let v = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 255]);
/// assert_eq!(format!("{:x}", v), "ff");
/// assert_eq!(format!("{:#x}", v), "0xff");
/// ```
impl fmt::LowerHex for U512 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Find the first non-zero limb scanning from MSB (index 7) down.
        let mut first_nonzero = None;
        let mut i = 7i32;
        while i >= 0 {
            if self.0[i as usize] != 0 {
                first_nonzero = Some(i as usize);
                break;
            }
            i -= 1;
        }

        match first_nonzero {
            None => {
                if f.alternate() {
                    f.write_str("0x0")
                } else {
                    f.write_str("0")
                }
            }
            Some(idx) => {
                if f.alternate() {
                    f.write_str("0x")?;
                }
                write!(f, "{:x}", self.0[idx])?;
                let mut j = idx;
                while j > 0 {
                    j -= 1;
                    write!(f, "{:016x}", self.0[j])?;
                }
                Ok(())
            }
        }
    }
}

#[cfg(test)]
mod ai_tests {
    use super::*;

    /// Zero formats as "0".
    #[test]
    fn zero() {
        assert_eq!(format!("{:x}", U512::from_be_limbs([0; 8])), "0");
    }

    /// Zero with alternate flag formats as "0x0".
    #[test]
    fn zero_alternate() {
        assert_eq!(format!("{:#x}", U512::from_be_limbs([0; 8])), "0x0");
    }

    /// Small value formats without leading zeros.
    #[test]
    fn small_value() {
        let v = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 0xAB]);
        assert_eq!(format!("{:x}", v), "ab");
    }

    /// Value spanning two limbs shows full padding on lower limb.
    #[test]
    fn two_limbs() {
        let v = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 1, 0]);
        assert_eq!(format!("{:x}", v), "10000000000000000");
    }

    /// All-max value fills all 128 hex digits.
    #[test]
    fn max_value() {
        let v = U512::from_be_limbs([u64::MAX; 8]);
        let s = format!("{:x}", v);
        assert_eq!(s.len(), 128);
        assert!(s.chars().all(|c| c == 'f'));
    }

    /// Alternate flag adds 0x prefix.
    #[test]
    fn alternate_prefix() {
        let v = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 0xCAFE]);
        assert_eq!(format!("{:#x}", v), "0xcafe");
    }
}
