pub mod entity;
mod group;

pub use group::SceneGroup;

use std::sync::atomic::{AtomicU32, Ordering};

use common::data::{level::LevelAnchorInfo, LEVEL_TABLE};
use proto::{AvatarType, VectorBin};

use self::entity::{EntityType, SceneActor, SceneEntity, SceneProp};

#[derive(Default)]
pub struct GameWorld {
    pub entry_id: u32,
    pub plane_id: u32,
    pub floor_id: u32,
    pub leader_entity_id: u32,
    pub scene_group_list: Vec<SceneGroup>,
    entity_counter: AtomicU32,
}

impl GameWorld {
    pub fn new(entry_id: u32, plane_id: u32, floor_id: u32) -> Self {
        Self {
            entry_id,
            plane_id,
            floor_id,
            leader_entity_id: 0,
            scene_group_list: Vec::new(),
            entity_counter: AtomicU32::new(0),
        }
    }

    pub fn is_actor(&self, entity_id: u32) -> bool {
        if let Some(group) = self.scene_group_list.iter().find(|g| g.id == 0) {
            group.actor_list.iter().any(|a| a.entity_id() == entity_id)
        } else {
            false
        }
    }

    pub fn add_player_actor(
        &mut self,
        uid: u32,
        avatar_id: u32,
        pos: Option<&VectorBin>,
        rot: Option<&VectorBin>,
    ) {
        let mut actor = SceneActor::new(
            EntityType::Actor as u32 * 1000000 + self.next_entity_id(),
            uid,
            avatar_id,
            AvatarType::AvatarFormalType.into(),
        );

        if let (Some(pos), Some(rot)) = (pos, rot) {
            actor.set_position(pos.x, pos.y, pos.z);
            actor.set_rotation(rot.x, rot.y, rot.z);
        } else {
            let anchor = self.get_player_start_position();
            actor.set_position(
                (anchor.pos_x * 1000.0) as i32,
                (anchor.pos_y * 1000.0) as i32,
                (anchor.pos_z * 1000.0) as i32,
            );
            actor.set_rotation(
                (anchor.rot_x * 1000.0) as i32,
                (anchor.rot_y * 1000.0) as i32,
                (anchor.rot_z * 1000.0) as i32,
            );
        }

        self.leader_entity_id = actor.entity_id();

        if let Some(group) = self.scene_group_list.iter_mut().find(|g| g.id == 0) {
            group.actor_list.push(actor);
        } else {
            self.scene_group_list.push(SceneGroup {
                id: 0,
                actor_list: vec![actor],
                prop_list: Vec::new(),
            });
        }
    }

    pub fn init_groups(&mut self) {
        let floor = LEVEL_TABLE.level_floors.get(&self.floor_id).unwrap();

        for group_instance in &floor.group_instance_list {
            let level_group = LEVEL_TABLE
                .get_level_group(self.floor_id, group_instance.id)
                .unwrap();

            let mut prop_entity_list = Vec::new();
            if let Some(prop_list) = level_group.prop_list.as_ref() {
                for level_prop in prop_list {
                    if !level_prop.load_on_initial {
                        continue;
                    }

                    // For now, spawn only calyxes
                    if level_prop.cocoon_id == 0 {
                        continue;
                    }

                    let mut prop = SceneProp::new(
                        EntityType::Prop as u32 * 1000000 + self.next_entity_id(),
                        group_instance.id,
                        level_prop.id,
                        level_prop.prop_id,
                    );

                    prop.set_position(
                        (level_prop.pos_x * 1000.0) as i32,
                        (level_prop.pos_y * 1000.0) as i32,
                        (level_prop.pos_z * 1000.0) as i32,
                    );
                    prop.set_rotation(
                        (level_prop.rot_x * 1000.0) as i32,
                        (level_prop.rot_y * 1000.0) as i32,
                        (level_prop.rot_z * 1000.0) as i32,
                    );

                    prop_entity_list.push(prop);
                }
            }

            self.scene_group_list.push(SceneGroup {
                id: group_instance.id,
                prop_list: prop_entity_list,
                actor_list: Vec::new(),
            });
        }
    }

    fn get_player_start_position(&self) -> &LevelAnchorInfo {
        let floor = LEVEL_TABLE.level_floors.get(&self.floor_id).unwrap();
        let group_instance = &floor.group_instance_list[floor.start_group_index as usize];

        let group = LEVEL_TABLE
            .get_level_group(self.floor_id, group_instance.id)
            .unwrap();
        &group
            .anchor_list
            .as_ref()
            .unwrap()
            .iter()
            .find(|a| a.id == floor.start_anchor_id)
            .unwrap()
    }

    fn next_entity_id(&self) -> u32 {
        self.entity_counter.fetch_add(1, Ordering::SeqCst) + 1
    }
}
