use std::any::{type_name, Any};
use std::fmt::Debug;

use seq_macro::seq;

use crate::event::component::{
    Added as ComponentAddedEvent,
    Kind as ComponentEventKind,
    Removed as ComponentRemovedEvent,
};
use crate::lock::{Error as LockError, Lock, ReadGuard, WriteGuard};
use crate::system::Input as SystemInput;
use crate::type_name::TypeName;
use crate::uid::Uid;
use crate::util::Array;
use crate::World;

pub mod local;

pub(crate) mod storage;

pub trait Component: SystemInput + Any + TypeName
{
    /// The component type in question. Will usually be `Self`
    type Component: Component
    where
        Self: Sized;

    type RefMut<'component>: FromOptionalMut<'component> + FromLockedOptional<'component>
    where
        Self: Sized;

    type Ref<'component>: FromOptional<'component> + FromLockedOptional<'component>
    where
        Self: Sized;

    /// Returns the ID of this component.
    fn id() -> Uid
    where
        Self: Sized;

    /// Returns the component UID of a component event for this component.
    fn get_event_uid(&self, event_kind: ComponentEventKind) -> Uid;

    #[doc(hidden)]
    fn as_any_mut(&mut self) -> &mut dyn Any;

    #[doc(hidden)]
    fn as_any(&self) -> &dyn Any;

    /// Returns whether the component `self` is optional.
    fn self_is_optional(&self) -> bool
    {
        false
    }

    /// Returns whether this component is optional.
    #[must_use]
    fn is_optional() -> bool
    where
        Self: Sized,
    {
        false
    }
}

impl dyn Component
{
    pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
    {
        self.as_any_mut().downcast_mut()
    }

    pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
    {
        self.as_any().downcast_ref()
    }

    pub fn is<Other: 'static>(&self) -> bool
    {
        self.as_any().is::<Other>()
    }
}

impl Debug for dyn Component
{
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
    {
        formatter.debug_struct("Component").finish_non_exhaustive()
    }
}

impl TypeName for Box<dyn Component>
{
    fn type_name(&self) -> &'static str
    {
        self.as_ref().type_name()
    }
}

impl<ComponentT> Component for Option<ComponentT>
where
    ComponentT: Component,
    for<'a> Option<ComponentT::Ref<'a>>: FromOptional<'a> + FromLockedOptional<'a>,
    for<'a> Option<ComponentT::RefMut<'a>>: FromOptionalMut<'a> + FromLockedOptional<'a>,
{
    type Component = ComponentT;
    type Ref<'component> = Option<ComponentT::Ref<'component>>;
    type RefMut<'component> = Option<ComponentT::RefMut<'component>>;

    fn id() -> Uid
    {
        ComponentT::id()
    }

    fn get_event_uid(&self, event_kind: ComponentEventKind) -> Uid
    {
        match event_kind {
            ComponentEventKind::Removed => ComponentRemovedEvent::<Self>::id(),
        }
    }

    fn as_any_mut(&mut self) -> &mut dyn Any
    {
        self
    }

    fn as_any(&self) -> &dyn Any
    {
        self
    }

    fn self_is_optional(&self) -> bool
    {
        true
    }

    fn is_optional() -> bool
    {
        true
    }
}

impl<ComponentT> TypeName for Option<ComponentT>
where
    ComponentT: Component,
{
    fn type_name(&self) -> &'static str
    {
        type_name::<Self>()
    }
}

impl<ComponentT> SystemInput for Option<ComponentT> where ComponentT: Component {}

/// A sequence of components.
pub trait Sequence
{
    /// The number of components in this component sequence.
    const COUNT: usize;

    type Array: Array<(Uid, Box<dyn Component>)>;

    fn into_array(self) -> Self::Array;

    fn metadata() -> impl Array<Metadata>;

    fn added_event_ids() -> Vec<Uid>;

    fn removed_event_ids() -> Vec<Uid>;
}

/// A mutable or immutable reference to a component.
pub trait Ref
{
    type Component: Component;
    type Handle<'component>: FromLockedOptional<'component>;
}

impl<ComponentT> Ref for &ComponentT
where
    ComponentT: Component,
{
    type Component = ComponentT;
    type Handle<'component> = ComponentT::Ref<'component>;
}

impl<ComponentT> Ref for &mut ComponentT
where
    ComponentT: Component,
{
    type Component = ComponentT;
    type Handle<'component> = ComponentT::RefMut<'component>;
}

/// [`Component`] metadata.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Metadata
{
    pub id: Uid,
    pub is_optional: bool,
}

impl Metadata
{
    #[must_use]
    pub fn new_non_optional(id: Uid) -> Self
    {
        Self { id, is_optional: false }
    }

    #[must_use]
    pub fn of<ComponentT: Component>() -> Self
    {
        Self {
            id: ComponentT::id(),
            is_optional: ComponentT::is_optional(),
        }
    }
}

pub trait FromOptionalMut<'comp>
{
    fn from_optional_mut_component(
        optional_component: Option<WriteGuard<'comp, Box<dyn Component>>>,
        world: &'comp World,
    ) -> Self;
}

pub trait FromOptional<'comp>
{
    fn from_optional_component(
        optional_component: Option<ReadGuard<'comp, Box<dyn Component>>>,
        world: &'comp World,
    ) -> Self;
}

pub trait FromLockedOptional<'comp>: Sized
{
    /// Converts a reference to a optional locked boxed component to a instance of `Self`.
    ///
    /// # Errors
    /// Returns `Err` if taking the lock (in a non-blocking way) fails.
    fn from_locked_optional_component(
        optional_component: Option<&'comp Lock<Box<dyn Component>>>,
        world: &'comp World,
    ) -> Result<Self, LockError>;
}

macro_rules! inner {
    ($c: tt) => {
        seq!(I in 0..=$c {
            impl<#(IntoComp~I,)*> Sequence for (#(IntoComp~I,)*)
                where
                #(
                    for<'comp> IntoComp~I: Into<
                        Component: Component<
                            RefMut<'comp>: FromOptionalMut<'comp>,
                            Ref<'comp>: FromOptional<'comp>
                        >
                    >,
                )*
            {
                const COUNT: usize = $c + 1;

                type Array = [(Uid, Box<dyn Component>); $c + 1];

                fn into_array(self) -> Self::Array
                {
                    [#({
                        let (id, component) = self.I.into_component();

                        (id, Box::new(component))
                    },)*]
                }

                fn metadata() -> impl Array<Metadata>
                {
                    [
                        #(
                            Metadata {
                                id: IntoComp~I::Component::id(),
                                is_optional: IntoComp~I::Component::is_optional(),
                            },
                        )*
                    ]
                }

                fn added_event_ids() -> Vec<Uid>
                {
                    vec![
                        #(ComponentAddedEvent::<IntoComp~I::Component>::id(),)*
                    ]
                }

                fn removed_event_ids() -> Vec<Uid>
                {
                    vec![
                        #(ComponentRemovedEvent::<IntoComp~I::Component>::id(),)*
                    ]
                }
            }
        });
    };
}

seq!(C in 0..=16 {
    inner!(C);
});

impl Sequence for ()
{
    type Array = [(Uid, Box<dyn Component>); 0];

    const COUNT: usize = 0;

    fn into_array(self) -> Self::Array
    {
        []
    }

    fn metadata() -> impl Array<Metadata>
    {
        []
    }

    fn added_event_ids() -> Vec<Uid>
    {
        Vec::new()
    }

    fn removed_event_ids() -> Vec<Uid>
    {
        Vec::new()
    }
}

pub trait Into
{
    type Component;

    fn into_component(self) -> (Uid, Self::Component);
}

impl<ComponentT> Into for ComponentT
where
    ComponentT: Component,
{
    type Component = Self;

    fn into_component(self) -> (Uid, Self::Component)
    {
        (Self::id(), self)
    }
}