use num_traits::Zero;

use super::{FixedDec, POS};

const INV_SIGN_MASK: u32 = (1 << 31) - 1;
const FRAC_BIT: u32 = 1 << 23;
const FRAC_MASK: u32 = FRAC_BIT - 1;

impl From<f32> for FixedDec {
    fn from(value: f32) -> Self {
        if value.is_zero() {
            return Self::zero();
        }
        let raw = value.to_bits() & INV_SIGN_MASK;
        let mut exp = (raw >> 23) as i32 - 127;
        let mut frac = raw & FRAC_MASK;
        let mut start = -exp;
        if exp == -127 {
            exp = -126;
            start = -exp;
        } else {
            frac += FRAC_BIT;
            start -= 1;
        }
        let end = -exp + 23;
        let start_i = start.div_euclid(32);
        let end_i = (end - 1).div_euclid(32);
        let mut parts = Vec::new();
        let mut dec = -start_i;
        if start_i == end_i {
            let val = frac << (8 - start.rem_euclid(32));
            if val != 0 {
                parts.push(val);
            }
        } else {
            let s = end.rem_euclid(32);
            let val_high = frac >> s;
            let val_low = frac << (32 - s);
            if val_high != 0 {
                parts.push(val_high);
            } else {
                dec -= 1;
            }
            if val_low != 0 {
                parts.push(val_low);
            }
        }
        if parts.is_empty() {
            dec = 0;
        }
        Self {
            sign: value.is_sign_negative(),
            dec,
            parts,
        }
    }
}

impl From<FixedDec> for f32 {
    fn from(value: FixedDec) -> Self {
        Self::from(&value)
    }
}

impl From<&FixedDec> for f32 {
    fn from(value: &FixedDec) -> Self {
        let sign = if value.is_neg() { 1 << 31 } else { 0 };

        let mut skip_count = 0;
        let mut iter = value.parts.iter().peekable();

        while let Some(0) = iter.peek() {
            skip_count += 1;
            iter.next();
        }

        let Some(v) = iter.next() else {
            return if value.is_pos() { 0.0 } else { -0.0 };
        };
        let mut start = v.leading_zeros() + 1;
        let exp_i = (value.dec - skip_count) * 32 - start as i32;
        let mut frac_sh = 0;
        let exp = if exp_i >= -127 {
            if exp_i == -127 {
                start -= 1;
            }
            (exp_i + 127) as u32
        } else {
            let sh = -(exp_i + 32 * 4 - 1);
            if sh < 23 {
                start -= 1;
                frac_sh = sh;
                0
            } else {
                return 0.0;
            }
        };
        let frac = if start > 9 {
            let sh = start - 9;
            (v << sh) + iter.next().copied().map(|v| v >> (32 - sh)).unwrap_or(0)
        } else {
            v >> (9 - start)
        } & !(1 << 23);
        let res = (frac >> frac_sh) + (exp << 23) + sign;
        f32::from_bits(res)
    }
}

impl From<u32> for FixedDec {
    fn from(value: u32) -> Self {
        Self {
            dec: 1,
            sign: POS,
            parts: vec![value],
        }
    }
}

impl From<i32> for FixedDec {
    fn from(value: i32) -> Self {
        Self {
            dec: 1,
            sign: value.is_negative(),
            parts: vec![value.try_into().unwrap_or((-value) as u32)],
        }
    }
}
