use std::{any::Any, sync::Arc};

use crate::{errors::CheckError, info::DisplayInfo};

use super::{Data, MersData, MersType, Type};

#[derive(Debug)]
pub struct Tuple(pub Vec<super::reference::Reference>);

impl Tuple {
    pub fn empty() -> Self {
        Self(vec![])
    }
    pub fn from(elems: impl IntoIterator<Item = Data>) -> Self {
        Self(
            elems
                .into_iter()
                .map(|d| super::reference::Reference::from(d))
                .collect(),
        )
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn get(&self, i: usize) -> Option<Data> {
        self.0.get(i).map(|v| v.read().clone())
    }
    pub fn get_mut(&self, i: usize) -> Option<super::reference::Reference> {
        self.0.get(i).map(|v| v.clone_ref())
    }
    pub fn clone_refs(&self) -> Self {
        Self(self.0.iter().map(|r| r.clone_ref()).collect())
    }
}

impl Clone for Tuple {
    fn clone(&self) -> Self {
        Self(self.0.iter().map(|r| r.clone_data()).collect())
    }
}

impl MersData for Tuple {
    fn display(&self, info: &DisplayInfo<'_>, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "(")?;
        for (i, c) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            c.read().get().display(info, f)?;
        }
        write!(f, ")")?;
        Ok(())
    }
    fn is_eq(&self, other: &dyn MersData) -> bool {
        if let Some(other) = other.as_any().downcast_ref::<Self>() {
            other.0 == self.0
        } else {
            false
        }
    }
    fn iterable(
        &self,
        _gi: &crate::program::run::RunLocalGlobalInfo,
    ) -> Option<Box<dyn Iterator<Item = Result<Data, CheckError>>>> {
        Some(Box::new(
            Clone::clone(self)
                .0
                .into_iter()
                .map(|r| r.read().clone())
                .map(Ok),
        ))
    }
    fn clone(&self) -> Box<dyn MersData> {
        Box::new(Clone::clone(self))
    }
    fn as_type(&self) -> Type {
        Type::new(TupleT(
            self.0.iter().map(|v| v.read().get().as_type()).collect(),
        ))
    }
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn mut_any(&mut self) -> &mut dyn Any {
        self
    }
    fn to_any(self) -> Box<dyn Any> {
        Box::new(self)
    }
}

#[derive(Debug)]
pub struct TupleT(pub Vec<Type>);
impl MersType for TupleT {
    fn display(
        &self,
        info: &crate::info::DisplayInfo<'_>,
        f: &mut std::fmt::Formatter,
    ) -> std::fmt::Result {
        write!(f, "(")?;
        for (i, c) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{}", c.with_display(info))?;
        }
        write!(f, ")")?;
        Ok(())
    }
    fn iterable(&self) -> Option<Type> {
        let mut o = Type::empty();
        for t in self.0.iter() {
            o.add_all(&t);
        }
        Some(o)
    }
    fn is_same_type_as(&self, other: &dyn MersType) -> bool {
        if let Some(other) = other.as_any().downcast_ref::<Self>() {
            self.0.len() == other.0.len()
                && self
                    .0
                    .iter()
                    .zip(other.0.iter())
                    .all(|(s, o)| s.is_same_type_as(o))
        } else {
            false
        }
    }
    fn is_included_in(&self, target: &dyn MersType) -> bool {
        if let Some(target) = target.as_any().downcast_ref::<Self>() {
            self.0.len() == target.0.len()
                && self
                    .0
                    .iter()
                    .zip(target.0.iter())
                    .all(|(s, t)| s.is_included_in(t))
        } else {
            false
        }
    }
    fn without(&self, remove: &dyn MersType) -> Option<Type> {
        let m = self.0.len();
        if let Some(remove) = remove
            .as_any()
            .downcast_ref::<Self>()
            .filter(|r| r.0.len() == m)
        {
            let mut out = Type::empty();
            for i1 in 0usize.. {
                let mut self_tuple = Vec::with_capacity(m);
                let mut i1 = i1;
                for j in 0..m {
                    let mm = self.0[j].types.len();
                    self_tuple.push(&self.0[j].types[i1 % mm]);
                    i1 /= mm;
                }
                if i1 != 0 {
                    break;
                }
                let mut covered = false;
                for i2 in 0usize.. {
                    let mut remove_tuple = Vec::with_capacity(m);
                    let mut i2 = i2;
                    for j in 0..m {
                        let mm = remove.0[j].types.len();
                        remove_tuple.push(&remove.0[j].types[i2 % mm]);
                        i2 /= mm;
                    }
                    if i2 != 0 {
                        break;
                    }
                    if (0..m).all(|j| {
                        self_tuple[j]
                            .as_ref()
                            .is_included_in(remove_tuple[j].as_ref())
                    }) {
                        covered = true;
                        break;
                    }
                }
                if !covered {
                    out.add(Arc::new(Self(
                        self_tuple
                            .iter()
                            .map(|v| Type::newm(vec![Arc::clone(v)]))
                            .collect(),
                    )));
                }
            }
            Some(out)
        } else {
            None
        }
    }
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn mut_any(&mut self) -> &mut dyn Any {
        self
    }
    fn to_any(self) -> Box<dyn Any> {
        Box::new(self)
    }
    fn simplify_for_display(&self, info: &crate::program::run::CheckInfo) -> Option<Type> {
        Some(Type::new(Self(
            self.0
                .iter()
                .map(|v| v.simplify_for_display(info))
                .collect(),
        )))
    }
}
