// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Typed list of values evaluation entity

use crate::{ty::*, value::*};
use derive_more::{Deref, DerefMut};

/// Collection of values of the same type.
#[derive(Clone, Deref, DerefMut)]
pub struct Array {
    /// List of values
    #[deref]
    #[deref_mut]
    items: ValueList,
    /// Element type.
    ty: Type,
}

impl Array {
    /// Create new list
    pub fn new(ty: Type) -> Self {
        Self {
            items: ValueList::default(),
            ty,
        }
    }

    /// Create new list from `ValueList`.
    pub fn from_values(items: ValueList, ty: Type) -> Self {
        Self { items, ty }
    }

    /// Fetch all values as `Vec<Value>`
    pub fn fetch(&self) -> Vec<Value> {
        self.items.iter().cloned().collect::<Vec<_>>()
    }
}

impl PartialEq for Array {
    fn eq(&self, other: &Self) -> bool {
        self.ty == other.ty && self.items == other.items
    }
}

impl IntoIterator for Array {
    type Item = Value;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.items.into_iter()
    }
}

impl TryFrom<ValueList> for Array {
    type Error = ValueError;
    fn try_from(items: ValueList) -> ValueResult<Array> {
        match items.types().common_type() {
            Some(ty) => Ok(Array::from_values(items, ty)),
            None => Err(ValueError::CommonTypeExpected),
        }
    }
}

impl FromIterator<Value> for Array {
    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
        let items: ValueList = iter.into_iter().collect();
        let ty = items.types().common_type().expect("Common type");
        Self { ty, items }
    }
}

impl std::fmt::Display for Array {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "[{items}]",
            items = self
                .items
                .iter()
                .map(|v| v.to_string())
                .collect::<Vec<_>>()
                .join(", ")
        )
    }
}

impl std::fmt::Debug for Array {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "[{items}]",
            items = self
                .items
                .iter()
                .map(|v| format!("{v:?}"))
                .collect::<Vec<_>>()
                .join(", ")
        )
    }
}

impl crate::ty::Ty for Array {
    fn ty(&self) -> Type {
        Type::Array(Box::new(self.ty.clone()))
    }
}

/// + operator. Adds a value to an array, e.g.: `[1,2] + 1 == [2,3]`.
impl std::ops::Add<Value> for Array {
    type Output = ValueResult;

    fn add(self, rhs: Value) -> Self::Output {
        if self.ty.is_compatible_to(&rhs.ty()) {
            Ok(Value::Array(Self::from_values(
                ValueList::new(
                    self.items
                        .iter()
                        .map(|value| value.clone() + rhs.clone())
                        .collect::<Result<Vec<_>, _>>()?,
                ),
                self.ty,
            )))
        } else {
            Err(ValueError::InvalidOperator("+".into()))
        }
    }
}

/// - operator. Subtracts a value from an array, e.g.: `[1,2] - 1 == [0,1]`.
impl std::ops::Sub<Value> for Array {
    type Output = ValueResult;

    fn sub(self, rhs: Value) -> Self::Output {
        if self.ty.is_compatible_to(&rhs.ty()) {
            Ok(Value::Array(Self::from_values(
                ValueList::new(
                    self.items
                        .iter()
                        .map(|value| value.clone() - rhs.clone())
                        .collect::<Result<Vec<_>, _>>()?,
                ),
                self.ty,
            )))
        } else {
            Err(ValueError::InvalidOperator("-".into()))
        }
    }
}

/// * operator. Multiply a value from an array, e.g.: `[1,2] * 2 == [2,4]`.
impl std::ops::Mul<Value> for Array {
    type Output = ValueResult;

    fn mul(self, rhs: Value) -> Self::Output {
        match self.ty {
            // List * Scalar or List * Integer
            Type::Quantity(_) | Type::Integer => Ok(Value::Array(Array::from_values(
                ValueList::new({
                    self.iter()
                        .map(|value| value.clone() * rhs.clone())
                        .collect::<Result<Vec<_>, _>>()?
                }),
                self.ty * rhs.ty().clone(),
            ))),
            _ => Err(ValueError::InvalidOperator("*".into())),
        }
    }
}

/// / operator. Divide an array by value, e.g.: `[2,4] / 2 == [1,2]`.
impl std::ops::Div<Value> for Array {
    type Output = ValueResult;

    fn div(self, rhs: Value) -> Self::Output {
        let values = ValueList::new(
            self.iter()
                .map(|value| value.clone() / rhs.clone())
                .collect::<Result<Vec<_>, _>>()?,
        );

        match (&self.ty, rhs.ty()) {
            // Integer / Integer => Scalar
            (Type::Integer, Type::Integer) => Ok(Value::Array(Array::from_values(
                values,
                self.ty / rhs.ty().clone(),
            ))),
            (Type::Quantity(_), _) => Ok(Value::Array(values.try_into()?)),
            _ => Err(ValueError::InvalidOperator("/".into())),
        }
    }
}

impl std::ops::Neg for Array {
    type Output = ValueResult;

    fn neg(self) -> Self::Output {
        let items: ValueList = self
            .iter()
            .map(|value| -value.clone())
            .collect::<Result<Vec<_>, _>>()?
            .into_iter()
            .collect();
        Ok(Value::Array(items.try_into()?))
    }
}

#[test]
fn test_array_debug() {
    let val1 = Value::Target(Target::new("my::name1".into(), Some("my::target1".into())));
    let val2 = Value::Target(Target::new("my::name2".into(), None));

    let mut array = Array::new(Type::Target);
    array.push(val1);
    array.push(val2);

    log::info!("{array}");
    log::info!("{array:?}");
}
