use std::collections::HashMap;

use anyhow::Result;
use assetpath::AssetPath;
use engine::prelude::*;

use crate::{animation_info::AnimationInfo, hitbox::HitBox};

use super::{entity_info::EntityInfo, entity_tags::EntityTags, entityparser::EntityParser};

pub struct EntityManager {
    entity_directory: AssetPath,
    resource_base_path: String,

    entity_file_list: Vec<String>,
    entity_file_map: HashMap<String, AssetPath>,
}

impl EntityManager {
    pub fn new(entity_directory: impl Into<AssetPath>) -> Result<Self> {
        let entity_directory = entity_directory.into();

        let resource_base_path = entity_directory.prefix().unwrap().to_string();

        let mut me = Self {
            entity_directory,
            resource_base_path,

            entity_file_list: Vec::new(),
            entity_file_map: HashMap::new(),
        };

        me.update_files()?;

        Ok(me)
    }

    /// Updates the lists of all '.gltf' and '.ent' files
    pub fn update_files(&mut self) -> Result<()> {
        let (entity_file_list, entity_file_map) =
            search_file_names(&self.entity_directory, ".ent")?;

        self.entity_file_list = entity_file_list;
        self.entity_file_map = entity_file_map;

        Ok(())
    }

    /// Returns all entity files
    pub fn entity_files(&self) -> &Vec<String> {
        &self.entity_file_list
    }

    /// Returns all found entity files with expected tags
    pub fn entity_files_with_tags(&self, tags: &[EntityTags]) -> Result<Vec<EntityInfo>> {
        let mut entity_files = Vec::new();

        if tags.is_empty() {
            return Ok(entity_files);
        }

        for entity_file in self.entity_file_list.iter() {
            // load entity file
            let file = self.load_entity_file(entity_file);
            let entity_parser = EntityParser::parse(file)?;

            // check for tags
            for tag in tags.iter() {
                if entity_parser.tags.contains(tag) {
                    entity_files.push(EntityInfo {
                        file_name: entity_file.clone(),
                        hitbox_radius: if entity_parser.enable_hitbox {
                            Some(entity_parser.hitbox_radius)
                        } else {
                            None
                        },
                        tags: entity_parser.tags,
                    });
                    break;
                }
            }
        }

        Ok(entity_files)
    }

    pub fn entity_info(&self, entity_file: &str) -> Result<EntityInfo> {
        let file = self.load_entity_file(entity_file);
        let entity_parser = EntityParser::parse(file)?;

        Ok(EntityInfo {
            file_name: entity_file.to_string(),
            hitbox_radius: if entity_parser.enable_hitbox {
                Some(entity_parser.hitbox_radius)
            } else {
                None
            },
            tags: entity_parser.tags,
        })
    }

    pub fn get_tags(&self, entity_file: &str) -> Result<Vec<EntityTags>> {
        // load entity file
        let file = self.load_entity_file(entity_file);
        let entity_parser = EntityParser::parse(file)?;

        Ok(entity_parser.tags)
    }

    fn load_entity_file(&self, entity_file: &str) -> &AssetPath {
        match self.entity_file_map.get(entity_file) {
            Some(file) => file,
            None => {
                // return Err(EngineError::FileNotFound(
                //     "Entity".to_string(),
                //     entity_file.to_string(),
                // ))

                todo!();
            }
        }
    }
}

impl AssetLoader for EntityManager {
    /// Loads an entity file and creates an Entity
    fn load_entity(
        &mut self,
        assets: &mut AssetHandler<'_>,
        entity_file: &str,
    ) -> Result<EntityObject> {
        // load entity file
        let file = self.load_entity_file(entity_file);

        #[allow(unused_mut)]
        let mut entity_parser = EntityParser::parse(file)?;

        // load asset

        let mut entity_object = assets.create_entity(&entity_parser.gltf_file_name)?;

        #[cfg(debug_assertions)]
        {
            entity_object.debug_name = Some(entity_file.to_string());
        }

        if entity_parser.enable_hitbox {
            entity_object.insert_component(HitBox::new(
                entity_parser.hitbox_radius,
                entity_parser.hitbox_height,
            ));
        }

        entity_parser
            .animation_map
            .retain(|_animation_type, indices| !indices.is_empty());

        if !entity_parser.animation_map.is_empty() {
            entity_object.insert_component(AnimationInfo::new(
                entity_parser.animations,
                entity_parser.animation_map,
            ));
        }

        if !entity_parser.sound_map.is_empty() {
            for audio_path in entity_parser.sound_map.values_mut() {
                if !audio_path.is_empty() {
                    audio_path.set_prefix(&self.resource_base_path);
                }
            }

            entity_object.insert_component(Audio::new(
                assets.world().resources.get_mut()?,
                entity_parser.sound_map,
            )?);
        }

        Ok(entity_object)
    }
}