use data::tables::{AvatarBaseID, MainCityBgmConfigID, SectionConfigID};
use proto::*;
use thiserror::Error;

use crate::logic::{
    math::Vector3f,
    scene::{SceneUnit, SceneUnitManager},
    time::MainCityTime,
    ESceneType,
};

use super::NapGameMode;

pub struct FrontendGame {
    section_id: SectionConfigID,
    frontend_avatar_id: AvatarBaseID,
    main_city_bgm: MainCityBgmConfigID,
    scene_unit_mgr: SceneUnitManager,
    camera_x: u32,
    camera_y: u32,
    entry_transform: String,
    main_city_time: MainCityTime,
    avatar_pos: Vector3f,
    avatar_rot: Vector3f,
}

#[derive(Error, Debug)]
pub enum FrontendGameError {
    #[error("player's frontend avatar is None")]
    NoFrontendAvatar,
}

impl FrontendGame {
    const DEFAULT_BGM_ID: u32 = 1005;

    pub fn new(
        section_id: SectionConfigID,
        avatar_id: AvatarBaseID,
        main_city_time: MainCityTime,
        avatar_pos: Vector3f,
        avatar_rot: Vector3f,
    ) -> Result<Self, FrontendGameError> {
        let instance = Self {
            section_id,
            main_city_time,
            main_city_bgm: MainCityBgmConfigID::new_unchecked(Self::DEFAULT_BGM_ID),
            scene_unit_mgr: SceneUnitManager::new(section_id),
            frontend_avatar_id: avatar_id,
            camera_x: 0xFFFFFFFF,
            camera_y: 0xFFFFFFFF,
            entry_transform: section_id.template().primary_entry_name.clone(),
            avatar_pos,
            avatar_rot,
        };

        tracing::info!("creating new frontend game (section={section_id}, avatar={avatar_id})");
        Ok(instance)
    }

    pub fn set_entry_transform(&mut self, transform_name: String) {
        self.entry_transform = transform_name;
    }
}

impl NapGameMode for FrontendGame {
    fn scene_info(&self) -> Option<SceneInfo> {
        Some(SceneInfo {
            scene_type: self.scene_type() as u32,
            hall_scene_info: Some(HallSceneInfo {
                section_id: self.section_id.value(),
                frontend_avatar_id: self.frontend_avatar_id.value(),
                main_city_bgm_id: self.main_city_bgm.value(),
                camera_x: self.camera_x,
                camera_y: self.camera_y,
                transform: self
                    .avatar_pos
                    .is_zero()
                    .then_some(self.entry_transform.clone())
                    .unwrap_or_default(),
                day_of_week: self.main_city_time.day_of_week.value(),
                time_of_day: self.main_city_time.time_of_day.value(),
                position: (!self.avatar_pos.is_zero()).then_some(Transform {
                    position: self.avatar_pos.to_vec(),
                    rotation: self.avatar_rot.to_vec(),
                }),
                scene_unit_list: self
                    .scene_unit_mgr
                    .unit_vec
                    .iter()
                    .map(SceneUnit::protocol_info)
                    .collect(),
                ..Default::default()
            }),
            ..Default::default()
        })
    }

    fn dungeon_info(&self) -> Option<DungeonInfo> {
        None
    }

    fn scene_type(&self) -> ESceneType {
        ESceneType::Hall
    }
}
