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

use data::math_def::{Coordinate, Vector2};
use num_enum::FromPrimitive;
use proto::Retcode;

use crate::entity::Entity;

use super::grid_mgr::GridMgr;

#[derive(Debug, Default, Clone, Copy, FromPrimitive)]
#[repr(u32)]
pub enum VisionLevelType {
    #[default]
    VisionLevelNormal,
    VisionLevelLittleRemote,
    VisionLevelRemote,
    VisionLevelSuper,
    VisionLevelNearby,
    VisionLevelSuperNearby,
}

pub struct SceneGridComp {
    begin_pos: Vector2,
    #[allow(unused)]
    scene_size: Vector2,
    grid_mgr_arr: [GridMgr; 6],
}

pub enum SceneSightComp {
    Grid(SceneGridComp),
}

impl SceneSightComp {
    pub fn new_grid(begin_pos: Vector2, scene_size: Vector2) -> Result<Self, Retcode> {
        let grid_comp = SceneGridComp::new(begin_pos, scene_size)?;
        Ok(Self::Grid(grid_comp))
    }

    pub fn place_entity(
        &mut self,
        entity: Rc<RefCell<dyn Entity>>,
    ) -> Result<Vec<Rc<RefCell<dyn Entity>>>, ()> {
        match self {
            Self::Grid(grid_comp) => grid_comp.place_entity(entity),
        }
    }

    pub fn remove_entity(&mut self, entity: &Rc<RefCell<dyn Entity>>) {
        match self {
            Self::Grid(grid_comp) => grid_comp.remove_entity(entity),
        }
    }

    pub fn pos_to_coordinate(&self, range_type: VisionLevelType, pos: &Vector2) -> Coordinate {
        match self {
            Self::Grid(grid_comp) => grid_comp.pos_to_coordinate(range_type, pos),
        }
    }

    pub fn get_vision_level_type(&self, entity: &Rc<RefCell<dyn Entity>>) -> VisionLevelType {
        match self {
            Self::Grid(grid_comp) => grid_comp.get_vision_level_type(entity),
        }
    }
}

impl SceneGridComp {
    pub fn new(begin_pos: Vector2, scene_size: Vector2) -> Result<Self, Retcode> {
        if scene_size.x == 0.0 || scene_size.y == 0.0 {
            tracing::error!("0 == scene_size_.x || 0 == scene_size_.y");
            return Err(Retcode::RetFail);
        }

        let mut grid_mgr_vec = Vec::with_capacity(6);
        for vision_level in 0..6 {
            let vision_level = vision_level.into();
            let grid_width = Self::get_grid_width(vision_level);
            let sight_range = Self::get_sight_range(vision_level);

            if grid_width == 0 || sight_range == 0 {
                tracing::error!("type: {vision_level:?} grid_width or sight_range invalid!");
                return Err(Retcode::RetFail);
            }

            if grid_width * (sight_range / grid_width) != sight_range {
                tracing::error!("{sight_range} / {grid_width} divide not exactly!");
                return Err(Retcode::RetFail);
            }

            let Ok(grid_mgr) =
                Self::create_grid_mgr(vision_level, grid_width, sight_range, &scene_size)
            else {
                tracing::error!("create_grid_mgr type: {vision_level:?} grid_width: {grid_width} sight_range: {sight_range} failed");
                return Err(Retcode::RetFail);
            };

            grid_mgr_vec.push(grid_mgr);
        }

        Ok(Self {
            begin_pos,
            scene_size,
            grid_mgr_arr: grid_mgr_vec.try_into().unwrap_or_else(|_| unreachable!()),
        })
    }

    fn create_grid_mgr(
        range_type: VisionLevelType,
        grid_width: u32,
        sight_range: u32,
        scene_size: &Vector2,
    ) -> Result<GridMgr, ()> {
        if grid_width == 0 {
            tracing::error!("invalid width: {grid_width}");
            return Err(());
        }

        let length =
            ((grid_width as f32 + scene_size.x - 1.0) / (grid_width as f32)).round() as u32;

        if length > 0xFFFF {
            tracing::error!("invalid length: {length}");
            return Err(());
        }

        let width = ((grid_width as f32 + scene_size.y - 1.0) / (grid_width as f32)).round() as u32;
        if width > 0xFFFF {
            tracing::error!("invalid width: {width}");
            return Err(());
        }

        Ok(GridMgr {
            range_type,
            length,
            width,
            sight_radius: sight_range,
            grid_map: HashMap::new(),
        })
    }

    pub fn place_entity(
        &mut self,
        entity: Rc<RefCell<dyn Entity>>,
    ) -> Result<Vec<Rc<RefCell<dyn Entity>>>, ()> {
        let vision_level_type = self.get_vision_level_type(&entity);

        let coordinate = {
            let entity = entity.borrow();
            let pos = entity.position();
            let pos = Vector2 { x: pos.x, y: pos.z };

            self.pos_to_coordinate(vision_level_type, &pos)
        };

        let grid_mgr = &mut self.grid_mgr_arr[vision_level_type as usize];
        let Ok(meet_entity_vec) = grid_mgr.place_entity(entity, coordinate) else {
            tracing::error!("range_type: {vision_level_type:?} place_entity failed");
            return Err(());
        };

        Ok(meet_entity_vec)
    }

    pub fn remove_entity(&mut self, entity: &Rc<RefCell<dyn Entity>>) {
        let vision_level_type = self.get_vision_level_type(entity);

        let coordinate = {
            let entity = entity.borrow();
            let pos = entity.position();
            let pos = Vector2 { x: pos.x, y: pos.z };

            self.pos_to_coordinate(vision_level_type, &pos)
        };

        let grid_mgr = &mut self.grid_mgr_arr[vision_level_type as usize];
        grid_mgr.remove_entity(entity, coordinate);
    }

    pub fn pos_to_coordinate(&self, range_type: VisionLevelType, pos: &Vector2) -> Coordinate {
        let grid_width = Self::get_grid_width(range_type);
        Coordinate {
            x: (pos.x - self.begin_pos.x) as i32 / grid_width as i32,
            y: (pos.y - self.begin_pos.y) as i32 / grid_width as i32,
        }
    }

    pub fn get_vision_level_type(&self, _entity: &Rc<RefCell<dyn Entity>>) -> VisionLevelType {
        // TODO!
        VisionLevelType::VisionLevelNormal
    }

    pub fn get_grid_width(vision_level: VisionLevelType) -> u32 {
        // SceneGridComp::scene_grid_width: dd 20, 50, 250, 1000, 2 dup(20)
        match vision_level {
            VisionLevelType::VisionLevelNormal => 20,
            VisionLevelType::VisionLevelLittleRemote => 50,
            VisionLevelType::VisionLevelRemote => 250,
            VisionLevelType::VisionLevelSuper => 1000,
            VisionLevelType::VisionLevelNearby => 20,
            VisionLevelType::VisionLevelSuperNearby => 20,
        }
    }

    pub fn get_sight_range(vision_level: VisionLevelType) -> u32 {
        match vision_level {
            VisionLevelType::VisionLevelNormal => 80,
            VisionLevelType::VisionLevelLittleRemote => 200,
            VisionLevelType::VisionLevelRemote => 1000,
            VisionLevelType::VisionLevelSuper => 4000,
            VisionLevelType::VisionLevelNearby => 40,
            VisionLevelType::VisionLevelSuperNearby => 40,
        }
    }
}

// TODO: this in gameserver config
/* <!-- 视野配置 -->
    <VisionList>
        <Vision level="正常视距" vision_range="80" grid_width="20" />
        <Vision level="较远视距" vision_range="160" grid_width="40" />
        <Vision level="远视距" vision_range="1000" grid_width="250" />
        <Vision level="超级视距" vision_range="4000" grid_width="1000" />
        <Vision level="近视距" vision_range="40" grid_width="20" />
    </VisionList>
*/
