use std::{
    cell::RefCell,
    collections::BTreeMap,
    rc::{Rc, Weak},
};

use data::math_def::Vector3;
use proto::{DungeonProductBin, EnterType, PlayerSceneCompBin, SceneProductBin, VisionType};
use tokio::sync::OnceCell;

use crate::player::Player;

use super::{PlayerWorld, Scene};

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
#[allow(non_camel_case_types)]
pub enum EnterSceneState {
    #[default]
    ENTER_SCENE_NONE,
    ENTER_SCENE_NOTIFY,
    ENTER_SCENE_READY,
    ENTER_SCENE_INIT,
    ENTER_SCENE_DONE,
    ENTER_SCENE_POST,
}

#[derive(Default)]
pub struct PlayerSceneComp {
    player: OnceCell<Weak<Player>>,
    pub own_world: Rc<RefCell<PlayerWorld>>,
    pub cur_scene_owner_uid: u32,
    pub my_prev_scene_id: u32,
    pub my_prev_pos: Vector3,
    pub my_prev_rot: Vector3,
    pub my_cur_scene_id: u32,
    pub monster_seen_list: Vec<u32>,
    //pub mark_point_list: Vec<MapMarkPoint>,
    pub world_product_map: BTreeMap<u32, SceneProductBin>,
    pub my_cur_area_id: u32,
    pub dungeon_product_map: BTreeMap<u32, DungeonProductBin>,
    pub my_cur_player_scene_id: u32,
    pub my_cur_home_scene_id: u32,
    pub cur_scene_id: u32,
    pub cur_scene_wtr: Weak<RefCell<Scene>>,
    pub dest_scene_id: u32,
    pub dest_pos: Vector3,
    pub dest_rot: Vector3,
    pub dest_enter_type: EnterType,
    pub dest_vision_type: VisionType,
    pub dest_world_ptr: Option<Rc<RefCell<PlayerWorld>>>,
    pub dest_scene_wtr: Weak<RefCell<Scene>>,
    pub enter_scene_state: EnterSceneState,
    pub enter_scene_token: u32,
}

impl PlayerSceneComp {
    pub fn set_player(&self, player: Rc<Player>) {
        self.own_world.borrow_mut().set_player(player.clone());
        self.player.set(Rc::downgrade(&player)).unwrap();
    }

    pub fn init(&mut self) {
        self.own_world.borrow_mut().init();
    }

    pub fn pre_login(&mut self, is_relogin: bool) {
        self.own_world.borrow_mut().pre_player_login(is_relogin);
    }

    pub fn on_login(&mut self, _is_new_player: bool) {
        self.reset_dest_scene();
    }

    pub fn on_first_login(&mut self) {
        let mut world = self.own_world.borrow_mut();
        let main_world_scene = world.scene_map.get_mut(&3).unwrap();
        main_world_scene
            .borrow_mut()
            .base_mut()
            .set_player_last_valid_location_on_first_login();
    }

    pub fn set_dest_scene(&mut self, scene_wtr: Weak<RefCell<Scene>>) {
        if let Some(scene) = scene_wtr.upgrade() {
            self.dest_scene_id = scene.borrow().base().scene_id;
            self.dest_scene_wtr = scene_wtr;
        } else {
            self.dest_scene_wtr = Weak::new();
            self.dest_scene_id = 0;
        }
    }

    pub fn get_dest_scene(&self) -> Option<Rc<RefCell<Scene>>> {
        self.dest_scene_wtr.upgrade()
    }

    pub fn get_cur_scene(&self) -> Option<Rc<RefCell<Scene>>> {
        self.cur_scene_wtr.upgrade()
    }

    fn reset_dest_scene(&mut self) {
        self.dest_scene_wtr = Weak::new();
        self.dest_scene_id = 0;
        self.dest_pos.reset();
        self.dest_rot.reset();
    }

    pub fn from_bin(bin: PlayerSceneCompBin) -> Self {
        Self {
            player: OnceCell::new(),
            own_world: Rc::new(RefCell::new(PlayerWorld::from_bin(
                bin.world.unwrap_or_default(),
            ))),
            cur_scene_owner_uid: bin.cur_scene_owner_uid,
            my_prev_scene_id: bin.my_prev_scene_id,
            my_prev_pos: Vector3::from_bin(bin.my_prev_pos.unwrap_or_default()),
            my_prev_rot: Vector3::from_bin(bin.my_prev_rot.unwrap_or_default()),
            my_cur_scene_id: bin.my_cur_scene_id,
            monster_seen_list: bin.monster_seen_list,
            world_product_map: bin.world_product_map.into_iter().collect(),
            my_cur_area_id: bin.my_cur_area_id,
            dungeon_product_map: bin.dungeon_product_map.into_iter().collect(),
            my_cur_player_scene_id: bin.my_cur_player_scene_id,
            my_cur_home_scene_id: bin.my_cur_home_scene_id,
            ..Default::default()
        }
    }

    pub fn to_bin(&self) -> PlayerSceneCompBin {
        PlayerSceneCompBin {
            world: Some(self.own_world.borrow().to_bin()),
            cur_scene_owner_uid: self.cur_scene_owner_uid.clone(),
            my_prev_scene_id: self.my_prev_scene_id.clone(),
            my_prev_pos: Some(self.my_prev_pos.to_bin()),
            my_prev_rot: Some(self.my_prev_rot.to_bin()),
            my_cur_scene_id: self.my_cur_scene_id.clone(),
            monster_seen_list: self.monster_seen_list.clone(),
            world_product_map: self.world_product_map.clone().into_iter().collect(),
            my_cur_area_id: self.my_cur_area_id.clone(),
            dungeon_product_map: self.dungeon_product_map.clone().into_iter().collect(),
            my_cur_player_scene_id: self.my_cur_player_scene_id.clone(),
            my_cur_home_scene_id: self.my_cur_home_scene_id.clone(),
            ..Default::default()
        }
    }
}
