use core::fmt;
use std::any::TypeId;
use std::{num::ParseIntError, str::FromStr};

use anyhow::Result;
use serde::{Deserialize, Serialize};

use crate::{ComponentDebug, ComponentNotFoundError, EntityComponent, MultiMut, TypeMap};

pub const COMPONENT_SEPARATOR: char = 'ยง';

#[derive(Default)]
pub struct ActivationState {
    activated: bool,
}

impl ActivationState {
    pub fn is_active(&self) -> bool {
        self.activated
    }

    pub(crate) fn apply_change(&mut self) {
        self.activated = !self.activated;
    }
}

pub struct EntityObject<S> {
    #[cfg(debug_assertions)]
    pub debug_name: Option<String>,

    // gltf file name
    pub(crate) gltf_file: Option<String>,

    // activation state of Entity
    pub(crate) activation_state: ActivationState,

    // program local ID
    pub entity_id: u32,

    // component map
    pub(crate) components: TypeMap<S>,

    #[cfg(debug_assertions)]
    component_names: Vec<String>,
}

unsafe impl<S> Send for EntityObject<S> {}
unsafe impl<S> Sync for EntityObject<S> {}

impl<S> EntityObject<S> {
    pub(crate) fn new(id: u32) -> Self {
        Self {
            #[cfg(debug_assertions)]
            debug_name: None,

            gltf_file: None,

            activation_state: ActivationState::default(),

            entity_id: id,

            components: TypeMap::default(),

            #[cfg(debug_assertions)]
            component_names: Vec::new(),
        }
    }

    pub fn gltf_file(&self) -> Option<&String> {
        self.gltf_file.as_ref()
    }

    pub fn multi_mut(&mut self) -> MultiMut<'_, S> {
        self.components.multi_mut()
    }

    pub fn insert_component<T: EntityComponent<S> + ComponentDebug>(
        &mut self,
        component: T,
    ) -> Option<T> {
        assert!(
            !self.activation_state.is_active(),
            "inserting components while the entity is activated is not allowed"
        );

        #[cfg(debug_assertions)]
        {
            let name = component.name().to_string();

            if !self.component_names.contains(&name) {
                self.component_names.push(name);
            }
        }

        self.components.insert(component)
    }

    pub(crate) fn insert_component_by_id(
        &mut self,
        type_id: TypeId,
        component: Box<dyn EntityComponent<S>>,
    ) -> Option<Box<dyn EntityComponent<S>>>
    where
        S: 'static,
    {
        assert!(
            !self.activation_state.is_active(),
            "inserting components while the entity is activated is not allowed"
        );

        #[cfg(debug_assertions)]
        {
            let name = component.name().to_string();

            if !self.component_names.contains(&name) {
                self.component_names.push(name);
            }
        }

        self.components.insert_type(type_id, component)
    }

    pub fn remove_component<T: EntityComponent<S>>(&mut self) -> Option<T> {
        assert!(
            !self.activation_state.is_active(),
            "removing components while the entity is activated is not allowed"
        );

        let t: Option<T> = self.components.remove();

        #[cfg(debug_assertions)]
        if let Some(t) = &t {
            if let Some(index) = self
                .component_names
                .iter()
                .position(|name| name == t.name())
            {
                self.component_names.remove(index);
            }
        }

        t
    }

    pub(crate) fn remove_component_by_id(
        &mut self,
        type_id: TypeId,
    ) -> Option<Box<dyn EntityComponent<S>>>
    where
        S: 'static,
    {
        assert!(
            !self.activation_state.is_active(),
            "removing components while the entity is activated is not allowed"
        );

        let t = self.components.remove_by_type_id(&type_id);

        #[cfg(debug_assertions)]
        if let Some(t) = &t {
            if let Some(index) = self
                .component_names
                .iter()
                .position(|name| name == t.name())
            {
                self.component_names.remove(index);
            }
        }

        t
    }

    #[cfg(debug_assertions)]
    pub fn component_names(&self) -> &[String] {
        &self.component_names
    }

    pub fn get_component<T: EntityComponent<S> + ComponentDebug>(
        &self,
    ) -> std::result::Result<&T, ComponentNotFoundError> {
        self.components.get()
    }

    pub fn get_component_mut<T: EntityComponent<S> + ComponentDebug>(
        &mut self,
    ) -> std::result::Result<&mut T, ComponentNotFoundError> {
        self.components.get_mut()
    }

    pub fn contains_component<T: EntityComponent<S>>(&self) -> bool {
        self.components.contains::<T>()
    }

    pub fn is_activated(&self) -> bool {
        self.activation_state.is_active()
    }

    pub fn as_entity(&self) -> Entity {
        Entity { id: self.entity_id }
    }

    pub(crate) fn clone_without_components(&self, id: u32) -> EntityObject<S> {
        EntityObject {
            #[cfg(debug_assertions)]
            debug_name: self.debug_name.clone(),

            // gltf file name
            gltf_file: self.gltf_file.clone(),

            activation_state: Default::default(),

            entity_id: id,

            // component map
            components: TypeMap::default(),

            #[cfg(debug_assertions)]
            component_names: Vec::new(),
        }
    }

    pub fn clone_component_from<T: EntityComponent<S> + ComponentDebug + Clone>(
        &mut self,
        other: &Self,
    ) -> Result<()> {
        let component = other.get_component::<T>()?;
        self.insert_component(component.clone());

        Ok(())
    }
}

#[allow(clippy::derive_hash_xor_eq)]
#[derive(Debug, Clone, Copy, Hash, Eq, Serialize, Deserialize)]
pub struct Entity {
    pub(crate) id: u32,
}

impl<S> PartialEq<EntityObject<S>> for Entity {
    fn eq(&self, entity: &EntityObject<S>) -> bool {
        self.id == entity.entity_id
    }
}

impl PartialEq<Entity> for Entity {
    fn eq(&self, entity: &Entity) -> bool {
        self.id == entity.id
    }
}

impl fmt::Display for Entity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.id)
    }
}

impl FromStr for Entity {
    type Err = ParseIntError;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        Ok(Self { id: s.parse()? })
    }
}