use crate::{
    error::{Error, SpanRange},
    interpreter::{
        data::{ArcisValue, BoolData, CollectionKind, CurveData, Data, ValData},
        data_store::SimpleAddress,
        library::cipher::x_to_pubkey,
        memory::Memory,
        pointer::Pointer,
        supported_type::{ArrayLen, SupportedType},
        Interpreter,
    },
};
use arcis_compiler::traits::ToMontgomery;

impl<'ast> Interpreter<'ast> {
    pub fn build_from_arcis_values(
        &mut self,
        values: &mut &[ArcisValue],
        ty: &SupportedType,
        enforce_bounds: bool,
        span_range: SpanRange,
    ) -> Result<SimpleAddress, Error> {
        let addr = match ty {
            SupportedType::Unit => Memory::SIMPLE_ADDRESS_OF_UNIT,
            SupportedType::FieldValue(r) => {
                let mut v = values[0];
                *values = &values[1..];
                if enforce_bounds {
                    v = r.cap_arcis_value(v);
                }
                self.memory.push(Data::Val(ValData(v, *r)))
            }
            SupportedType::Bool => {
                let v = values[0];
                *values = &values[1..];
                let b = if enforce_bounds {
                    BoolData::new_with_cap(v)
                } else {
                    BoolData(v.into())
                };
                self.memory.push(Data::Bool(b))
            }
            SupportedType::Curve(ty) => {
                let v = values[0];
                *values = &values[1..];
                let c = x_to_pubkey(v);
                self.memory.push(Data::Curve(CurveData(c, *ty)))
            }
            SupportedType::Ref(ty) => {
                let addr = self.build_from_arcis_values(values, ty, enforce_bounds, span_range)?;
                self.memory.push(Data::Pointer(addr.as_pointer()))
            }
            SupportedType::Struct(s) => {
                let v: Result<Vec<_>, _> = s
                    .types()
                    .iter()
                    .map(|x| self.build_from_arcis_values(values, x, enforce_bounds, span_range))
                    .collect();
                let v = v?;
                self.memory
                    .push(Data::Collection(v, CollectionKind::Struct(s.clone())))
            }
            SupportedType::Array(ty, n) => {
                let val = n.value().map_err(|e| Error::new(span_range, e))?;
                let v: Result<Vec<_>, _> = (0..val)
                    .map(|_| self.build_from_arcis_values(values, ty, enforce_bounds, span_range))
                    .collect();
                let v = v?;
                self.memory
                    .push(Data::Collection(v, CollectionKind::Array(*ty.clone())))
            }
            SupportedType::Tuple(types) => {
                let v: Result<Vec<_>, _> = types
                    .iter()
                    .map(|ty| self.build_from_arcis_values(values, ty, enforce_bounds, span_range))
                    .collect();
                let v = v?;
                self.memory.push(Data::Collection(v, CollectionKind::Tuple))
            }
            SupportedType::Slice(ty, n) => {
                let len = n.value().map_err(|_| {
                    Error::new(span_range, "Slices cannot be built from arcis values.")
                })?;
                let ty = SupportedType::Array(ty.clone(), ArrayLen::Computed(len));
                self.build_from_arcis_values(values, &ty, enforce_bounds, span_range)?
            }
            _ => {
                return Err(Error::new(
                    span_range,
                    format!("Types like `{}` cannot be built from numbers.", ty),
                ))
            }
        };
        Ok(addr)
    }
    pub fn data_to_arcis_values_impl(
        &mut self,
        data: Data,
        acc: &mut Vec<ArcisValue>,
    ) -> Result<(), Error> {
        match data {
            Data::Val(v) => acc.push(v.val()),
            Data::Bool(BoolData(b)) => acc.push(ArcisValue::from(b)),
            Data::Curve(c) => acc.push(c.0.to_montgomery(false).0),
            Data::Pointer(p) => {
                let data = self.memory.compute(p)?;
                self.data_to_arcis_values_impl(data, acc)?;
            }
            Data::Collection(arr, _) => {
                for addr in arr {
                    let data = self.memory[addr].clone();
                    self.data_to_arcis_values_impl(data, acc)?;
                }
            }
            Data::Unit => {}  // No expressions.
            Data::Fn(_) => {} // No expressions.
        }
        Ok(())
    }
    pub fn pointer_to_arcis_values(&mut self, pointer: Pointer) -> Result<Vec<ArcisValue>, Error> {
        let mut values = Vec::new();
        self.data_to_arcis_values_impl(pointer.as_data(), &mut values)?;
        Ok(values)
    }
}
