use crate::{
    core::{
        circuits::boolean::{boolean_value::BooleanValue, byte::Byte},
        global_value::value::FieldValue,
    },
    traits::{FromLeBits, GetBit},
    utils::{field::BaseField, used_field::UsedField},
};
use core::panic;
use ff::Field;

#[derive(Debug, Clone)]
pub struct F64 {
    pub sign: BooleanValue,
    pub exponent: [BooleanValue; 11],
    pub mantissa: FieldValue<BaseField>,
}

impl F64 {
    #[allow(dead_code)]
    pub fn new(
        sign: BooleanValue,
        exponent: [BooleanValue; 11],
        mantissa: FieldValue<BaseField>,
    ) -> Self {
        let bounds = mantissa.bounds();
        if bounds.unsigned_min() != BaseField::ZERO
            || bounds.unsigned_max() != BaseField::power_of_two(52) - BaseField::ONE
        {
            panic!(
                "Invalid bounds for the mantissa. Expected (0, 2^52-1) (found {:?} and {:?})",
                bounds.unsigned_min(),
                bounds.unsigned_max()
            );
        }

        Self {
            sign,
            exponent,
            mantissa,
        }
    }

    pub fn from_le_bytes(bytes: [Byte<BooleanValue>; 8]) -> Self {
        let mut bits = bytes
            .into_iter()
            .flat_map(|byte| byte.get_bits())
            .collect::<Vec<BooleanValue>>();
        let sign = bits.pop().unwrap();
        let exponent = bits
            .split_off(52)
            .try_into()
            .unwrap_or_else(|v: Vec<BooleanValue>| {
                panic!("Expected a Vec of length 11 (found {})", v.len())
            });
        let mantissa = FieldValue::<BaseField>::from_le_bits(bits, false);

        Self {
            sign,
            exponent,
            mantissa,
        }
    }

    pub fn to_le_bytes(&self) -> [Byte<BooleanValue>; 8] {
        let mut mantissa_bits = (0..52)
            .map(|i| self.mantissa.get_bit(i, false))
            .collect::<Vec<BooleanValue>>();
        mantissa_bits.append(&mut self.exponent.to_vec());
        mantissa_bits.push(self.sign);
        mantissa_bits
            .chunks(8)
            .map(|bits| {
                Byte::new(
                    bits.to_vec()
                        .try_into()
                        .unwrap_or_else(|v: Vec<BooleanValue>| {
                            panic!("Expected a Vec of length 8 (found {})", v.len())
                        }),
                )
            })
            .collect::<Vec<Byte<BooleanValue>>>()
            .try_into()
            .unwrap_or_else(|v: Vec<Byte<BooleanValue>>| {
                panic!("Expected a Vec of length 8 (found {})", v.len())
            })
    }
}
