use std::{
    any::Any,
    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
};

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

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

#[derive(Debug)]
pub struct Reference(Arc<RwLock<Data>>);

impl Reference {
    /// Creates a reference from data,
    /// so that the original data can't be
    /// modified through the new reference.
    pub fn from(data: Data) -> Self {
        Self(Arc::new(RwLock::new(data.mkref().0)))
    }
    pub fn raw(reference: Arc<RwLock<Data>>) -> Self {
        Self(reference)
    }
    pub fn read(&'_ self) -> RwLockReadGuard<'_, Data> {
        self.0.read().unwrap()
    }
    pub fn write(&'_ self) -> RwLockWriteGuard<'_, Data> {
        self.0.write().unwrap()
    }
}

impl MersData for Reference {
    fn display(&self, info: &DisplayInfo<'_>, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.0.read().unwrap().get().display(info, f)
    }
    fn executable(&self) -> Option<crate::data::function::FunctionT> {
        let inner = self.0.read().unwrap();
        let inner = inner.get();
        if let Some(func) = inner
            .as_ref()
            .as_any()
            .downcast_ref::<crate::data::function::Function>()
        {
            Some(func.get_as_type())
        } else {
            None
        }
    }
    fn execute(
        &self,
        arg: Data,
        gi: &crate::program::run::RunLocalGlobalInfo,
    ) -> Option<Result<Data, CheckError>> {
        let mut inner = self.0.write().unwrap();
        let mut inner = inner.get_mut();
        if let Some(func) = inner
            .as_mut()
            .mut_any()
            .downcast_mut::<crate::data::function::Function>()
        {
            Some(func.run_mut(arg, gi.clone()))
        } else {
            None
        }
    }
    fn iterable(
        &self,
        gi: &crate::program::run::RunLocalGlobalInfo,
    ) -> Option<Box<dyn Iterator<Item = Result<Data, CheckError>>>> {
        let inner = Arc::clone(&self.0);
        let gi = gi.clone();
        Some(Box::new(std::iter::from_fn(move || {
            match inner
                .write()
                .unwrap()
                .get_mut()
                .mut_any()
                .downcast_mut::<crate::data::function::Function>()
                .unwrap()
                .run_mut(Data::empty_tuple(), gi.clone())
            {
                Err(e) => Some(Err(e)),
                Ok(v) => {
                    if let Some(v) = v.one_tuple_content() {
                        Some(Ok(v))
                    } else {
                        None
                    }
                }
            }
        })))
    }
    fn is_eq(&self, other: &dyn MersData) -> bool {
        if let Some(other) = other.as_any().downcast_ref::<Self>() {
            *other.0.write().unwrap() == *self.0.write().unwrap()
        } else {
            false
        }
    }
    fn clone(&self) -> Box<dyn MersData> {
        Box::new(self.clone_ref())
    }
    fn as_type(&self) -> Type {
        Type::new(ReferenceT(self.0.read().unwrap().get().as_type()))
    }
    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)
    }
}

impl Reference {
    pub fn clone_ref(&self) -> Self {
        Self(Arc::clone(&self.0))
    }
    pub fn clone_data(&self) -> Self {
        Self(Arc::new(RwLock::new(
            self.0.read().unwrap().clone().mkref().0,
        )))
    }
}

impl PartialEq for Reference {
    fn eq(&self, other: &Self) -> bool {
        *self.0.read().unwrap() == *other.0.read().unwrap()
    }
}

#[derive(Debug, Clone)]
pub struct ReferenceT(pub Type);
impl MersType for ReferenceT {
    fn display(
        &self,
        info: &crate::info::DisplayInfo<'_>,
        f: &mut std::fmt::Formatter,
    ) -> std::fmt::Result {
        if self.0.types.len() > 1 {
            write!(f, "&{{{}}}", self.0.with_display(info))
        } else {
            write!(f, "&{}", self.0.with_display(info))
        }
    }
    fn executable(&self) -> Option<crate::data::function::FunctionT> {
        let mut funcs: Vec<crate::data::function::FunctionT> = vec![];
        for func in self.0.types.iter() {
            funcs.push(Clone::clone(
                func.as_any()
                    .downcast_ref::<crate::data::function::FunctionT>()?,
            ));
        }
        Some(super::function::FunctionT(
            Ok(Arc::new(move |a, _| {
                let mut out = Type::empty();
                for func in funcs.iter() {
                    out.add_all(&func.o(a)?);
                }
                Ok(out)
            })),
            crate::info::Info::neverused(),
        ))
    }
    fn iterable(&self) -> Option<Type> {
        let mut out = Type::empty();
        for func in self.0.types.iter() {
            out.add_all(
                &func
                    .as_any()
                    .downcast_ref::<crate::data::function::FunctionT>()?
                    .iterable()?,
            );
        }
        if !out.types.is_empty() {
            Some(out)
        } else {
            None
        }
    }
    fn is_same_type_as(&self, other: &dyn MersType) -> bool {
        if let Some(o) = other.as_any().downcast_ref::<Self>() {
            self.0.is_same_type_as(&o.0)
        } else {
            false
        }
    }
    fn is_included_in(&self, target: &dyn MersType) -> bool {
        // &int isn't included in &(int/float), otherwise we could assign a float to it
        self.is_same_type_as(target)
    }
    fn without(&self, remove: &dyn MersType) -> Option<Type> {
        if self.is_included_in(remove) {
            Some(Type::empty())
        } 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 is_reference_to(&self) -> Option<&Type> {
        Some(&self.0)
    }
}
