use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::ecs::render_layer::components::RenderLayer;
use crate::ecs::sprite::components::Sprite;
use crate::ecs::sprite_animator::components::SpriteAnimator;
use crate::ecs::sprite_particles::components::SpriteParticleEmitter;
use crate::ecs::tilemap::components::Tilemap;
use crate::ecs::tween::components::Tween;
use crate::ecs::visibility::components::Visibility;

pub const SCENE_2D_FORMAT_VERSION: u32 = 1;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Scene2D {
    pub format_version: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    pub entities: Vec<SceneEntity2D>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub metadata: HashMap<String, serde_json::Value>,
}

impl Scene2D {
    pub fn new(name: Option<&str>) -> Self {
        Self {
            format_version: SCENE_2D_FORMAT_VERSION,
            name: name.map(|name| name.to_string()),
            entities: Vec::new(),
            metadata: HashMap::new(),
        }
    }

    pub fn add_entity(&mut self, entity: SceneEntity2D) -> u32 {
        let id = entity.id;
        self.entities.push(entity);
        id
    }

    pub fn find_entity(&self, id: u32) -> Option<&SceneEntity2D> {
        self.entities.iter().find(|entity| entity.id == id)
    }

    pub fn find_entity_mut(&mut self, id: u32) -> Option<&mut SceneEntity2D> {
        self.entities.iter_mut().find(|entity| entity.id == id)
    }

    pub fn root_entities(&self) -> impl Iterator<Item = &SceneEntity2D> {
        self.entities
            .iter()
            .filter(|entity| entity.parent_id.is_none())
    }

    pub fn children_of(&self, parent_id: u32) -> impl Iterator<Item = &SceneEntity2D> {
        self.entities
            .iter()
            .filter(move |entity| entity.parent_id == Some(parent_id))
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneEntity2D {
    pub id: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub parent_id: Option<u32>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sprite: Option<Sprite>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tilemap: Option<Tilemap>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sprite_animator: Option<SpriteAnimator>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tween: Option<Tween>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sprite_particle_emitter: Option<SpriteParticleEmitter>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub visibility: Option<Visibility>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub render_layer: Option<RenderLayer>,
}

impl SceneEntity2D {
    pub fn new(id: u32) -> Self {
        Self {
            id,
            parent_id: None,
            name: None,
            sprite: None,
            tilemap: None,
            sprite_animator: None,
            tween: None,
            sprite_particle_emitter: None,
            visibility: None,
            render_layer: None,
        }
    }

    pub fn with_name(mut self, name: impl Into<String>) -> Self {
        self.name = Some(name.into());
        self
    }

    pub fn with_parent(mut self, parent_id: u32) -> Self {
        self.parent_id = Some(parent_id);
        self
    }

    pub fn with_sprite(mut self, sprite: Sprite) -> Self {
        self.sprite = Some(sprite);
        self
    }

    pub fn with_tilemap(mut self, tilemap: Tilemap) -> Self {
        self.tilemap = Some(tilemap);
        self
    }

    pub fn with_sprite_animator(mut self, sprite_animator: SpriteAnimator) -> Self {
        self.sprite_animator = Some(sprite_animator);
        self
    }

    pub fn with_tween(mut self, tween: Tween) -> Self {
        self.tween = Some(tween);
        self
    }

    pub fn with_sprite_particle_emitter(mut self, emitter: SpriteParticleEmitter) -> Self {
        self.sprite_particle_emitter = Some(emitter);
        self
    }

    pub fn with_visibility(mut self, visibility: Visibility) -> Self {
        self.visibility = Some(visibility);
        self
    }

    pub fn with_render_layer(mut self, render_layer: RenderLayer) -> Self {
        self.render_layer = Some(render_layer);
        self
    }
}
