use std::{
    collections::HashMap,
    sync::{Arc, Mutex},
};

use crate::info::DisplayInfo;

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

#[derive(Debug, PartialEq)]
pub struct Object(Vec<(usize, super::reference::Reference)>);
impl Object {
    pub fn new(v: impl IntoIterator<Item = (usize, Data)>) -> Self {
        Self(
            v.into_iter()
                .map(|(i, d)| (i, super::reference::Reference::from(d)))
                .collect(),
        )
    }
    pub fn get(&self, f: usize) -> Option<Data> {
        self.iter().find(|v| v.0 == f).map(|v| v.1.read().clone())
    }
    pub fn get_mut(&self, f: usize) -> Option<&super::reference::Reference> {
        self.iter().find(|v| v.0 == f).map(|(_, v)| v)
    }
    pub fn iter(&'_ self) -> std::slice::Iter<'_, (usize, super::reference::Reference)> {
        self.0.iter()
    }
    pub fn clone_refs(&self) -> Self {
        Self(self.0.iter().map(|(i, r)| (*i, r.clone_ref())).collect())
    }
}
impl Clone for Object {
    fn clone(&self) -> Self {
        Self(self.0.iter().map(|(i, r)| (*i, r.clone_data())).collect())
    }
}
#[derive(Debug, Clone)]
pub struct ObjectT(Vec<(usize, Type)>);
impl ObjectT {
    pub fn new(v: Vec<(usize, Type)>) -> Self {
        Self(v)
    }
    pub fn get(&self, f: usize) -> Option<&Type> {
        self.iter().find(|v| v.0 == f).map(|v| &v.1)
    }
    pub fn iter(&'_ self) -> std::slice::Iter<'_, (usize, Type)> {
        self.0.iter()
    }
    fn len(&self) -> usize {
        self.0.len()
    }
}

impl MersData for Object {
    fn display(&self, info: &DisplayInfo<'_>, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let mut comma_sep = false;
        write!(f, "{{")?;
        for (field, val) in self.iter() {
            if comma_sep {
                write!(f, ", ")?;
            }
            write!(
                f,
                "{}: {}",
                info.get_object_field_name(*field),
                val.read().get().with_display(info)
            )?;
            comma_sep = true;
        }
        write!(f, "}}")?;
        Ok(())
    }
    fn is_eq(&self, other: &dyn MersData) -> bool {
        if let Some(other) = other.as_any().downcast_ref::<Self>() {
            self == other
        } else {
            false
        }
    }
    fn clone(&self) -> Box<dyn MersData> {
        Box::new(Clone::clone(self))
    }
    fn as_type(&self) -> Type {
        Type::new(ObjectT(
            self.iter()
                .map(|(n, v)| (n.clone(), v.read().get().as_type()))
                .collect(),
        ))
    }
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
    fn mut_any(&mut self) -> &mut dyn std::any::Any {
        self
    }
    fn to_any(self) -> Box<dyn std::any::Any> {
        Box::new(self)
    }
}

impl MersType for ObjectT {
    fn display(
        &self,
        info: &crate::info::DisplayInfo<'_>,
        f: &mut std::fmt::Formatter,
    ) -> std::fmt::Result {
        let mut comma_sep = false;
        write!(f, "{{")?;
        for (field, t) in self.iter() {
            if comma_sep {
                write!(f, ", ")?;
            }
            write!(
                f,
                "{}: {}",
                info.get_object_field_name(*field),
                t.with_display(info)
            )?;
            comma_sep = true;
        }
        write!(f, "}}")?;
        Ok(())
    }
    fn is_same_type_as(&self, other: &dyn MersType) -> bool {
        other.as_any().downcast_ref::<Self>().is_some_and(|other| {
            self.len() == other.len()
                && other.iter().all(|(field, target_type)| {
                    self.get(*field)
                        .is_some_and(|self_type| self_type.is_same_type_as(target_type))
                })
        })
    }
    fn is_included_in(&self, target: &dyn MersType) -> bool {
        target
            .as_any()
            .downcast_ref::<Self>()
            .is_some_and(|target| {
                self.len() >= target.len()
                    && target.iter().all(|(field, target_type)| {
                        self.get(*field)
                            .is_some_and(|self_type| self_type.is_included_in(target_type))
                    })
            })
    }
    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].1.types.len();
                    self_tuple.push((self.0[j].0, &self.0[j].1.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..remove.0.len() {
                        let mm = remove.0[j].1.types.len();
                        remove_tuple.push((remove.0[j].0, &remove.0[j].1.types[i2 % mm]));
                        i2 /= mm;
                    }
                    if i2 != 0 {
                        break;
                    }
                    if (0..m).all(|j| {
                        remove_tuple
                            .iter()
                            .find(|(v, _)| *v == self_tuple[j].0)
                            .is_some_and(|(_, r)| {
                                self_tuple[j].1.as_ref().is_included_in(r.as_ref())
                            })
                    }) {
                        covered = true;
                        break;
                    }
                }
                if !covered {
                    out.add(Arc::new(Self(
                        self_tuple
                            .iter()
                            .map(|(vi, v)| (*vi, Type::newm(vec![Arc::clone(v)])))
                            .collect(),
                    )));
                }
            }
            Some(out)
        } else {
            None
        }
    }
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
    fn mut_any(&mut self) -> &mut dyn std::any::Any {
        self
    }
    fn to_any(self) -> Box<dyn std::any::Any> {
        Box::new(self)
    }
    fn simplify_for_display(&self, info: &crate::program::run::CheckInfo) -> Option<Type> {
        Some(Type::new(Self(
            self.0
                .iter()
                .map(|(n, t)| (n.clone(), t.simplify_for_display(info)))
                .collect(),
        )))
    }
}

pub trait ObjectFieldsMap {
    fn get_or_add_field(&self, field: &str) -> usize;
}
impl ObjectFieldsMap for Arc<Mutex<HashMap<String, usize>>> {
    fn get_or_add_field(&self, field: &str) -> usize {
        let mut s = self.lock().unwrap();
        if let Some(f) = s.get(field) {
            return *f;
        }
        let o = s.len();
        s.insert(field.to_owned(), o);
        o
    }
}
