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

use data::math_def::Coordinate;
use proto::ProtEntityType;

use crate::entity::Entity;

use super::{grid::Grid, region::Region, visitor::Visitor, VisionLevelType};

#[allow(unused)]
pub struct GridMgr {
    pub range_type: VisionLevelType,
    pub length: u32,
    pub width: u32,
    pub sight_radius: u32,
    pub grid_map: HashMap<(i32, i32), Grid>,
}

impl GridMgr {
    pub fn get_grid(&mut self, x: i32, y: i32) -> &mut Grid {
        self.grid_map.entry((x, y)).or_insert_with(Grid::default)
    }

    pub fn find_grid(&self, x: i32, y: i32) -> Option<&Grid> {
        self.grid_map.get(&(x, y))
    }

    pub fn place_entity(
        &mut self,
        entity: Rc<RefCell<dyn Entity>>,
        coordinate: Coordinate,
    ) -> Result<Vec<Rc<RefCell<dyn Entity>>>, ()> {
        let entity_type = {
            let entity = entity.borrow();
            entity.get_entity_type()
        };

        let grid = self.get_grid(coordinate.x, coordinate.y);
        if entity_type == ProtEntityType::ProtEntityRegion {
            tracing::error!("GridMgr::place_entity shouldn't be used for Region entity");
            return Err(());
        } else {
            grid.add_entity(entity);
        }

        Ok(grid.get_all_entity())
    }

    pub fn remove_entity(&mut self, entity: &Rc<RefCell<dyn Entity>>, coordinate: Coordinate) {
        let grid = self.get_grid(coordinate.x, coordinate.y);
        grid.del_entity(entity);
    }

    pub fn entity_move_to(&mut self, entity: &Rc<RefCell<dyn Entity>>, coordinate: &Coordinate) {
        let prev_coordinate = entity.borrow().get_coordinate();
        if *coordinate == prev_coordinate {
            return;
        }

        let prev_grid = self.get_grid(prev_coordinate.x, prev_coordinate.y);
        prev_grid.del_entity(entity);

        let grid = self.get_grid(coordinate.x, coordinate.y);
        grid.add_entity(entity.clone());

        entity.borrow_mut().set_coordinate(coordinate.clone());
    }

    pub fn visit_diff_grids(
        &self,
        center1: &Coordinate,
        center2: &Coordinate,
        t1: &mut Visitor,
        t2: &mut Visitor,
    ) {
        let sight_radius = self.sight_radius as i32;

        let left_x1 = center1.x - sight_radius;
        let right_x1 = center1.x + sight_radius;
        let low_y1 = center1.y - sight_radius;
        let up_y1 = center1.y + sight_radius;

        let left_x2 = center2.x - sight_radius;
        let right_x2 = center2.x + sight_radius;
        let low_y2 = center2.y - sight_radius;
        let up_y2 = center2.y + sight_radius;

        if right_x1 >= left_x2 && right_x2 >= left_x1 && up_y1 >= low_y2 && up_y2 >= low_y1 {
            let mut low_y = low_y1;
            let mut up_y = up_y1;

            if center1.y >= center2.y {
                if center1.y > center2.y {
                    for j in up_y1..up_y2 {
                        for i in left_x1..=right_x1 {
                            self.visit_grid(i, j, t1);
                        }
                    }
                    for j in low_y2..low_y1 {
                        for i in left_x2..=right_x2 {
                            self.visit_grid(i, j, t2);
                        }
                    }

                    low_y = low_y1;
                    up_y = up_y2;
                }
            } else {
                for j in low_y1..low_y2 {
                    for i in left_x1..=right_x1 {
                        self.visit_grid(i, j, t1);
                    }
                }
                for j in up_y2..up_y1 {
                    for i in left_x2..=right_x2 {
                        self.visit_grid(i, j, t2);
                    }
                }

                low_y = low_y2;
                up_y = up_y1;
            }

            for j in low_y..=up_y {
                if center1.x >= center2.x {
                    if center1.x > center2.x {
                        for i in left_x2..left_x1 {
                            self.visit_grid(i, j, t2);
                        }
                        for i in right_x1..right_x2 {
                            self.visit_grid(i, j, t1);
                        }
                    }
                } else {
                    for i in left_x1..left_x2 {
                        self.visit_grid(i, j, t1);
                    }
                    for i in right_x2..right_x1 {
                        self.visit_grid(i, j, t2);
                    }
                }
            }
        } else {
            for i in left_x1..=right_x1 {
                for j in low_y1..=up_y1 {
                    self.visit_grid(i, j, t1);
                }
            }
            for i in left_x2..=right_x2 {
                for j in low_y2..=up_y2 {
                    self.visit_grid(i, j, t2);
                }
            }
        }
    }

    pub fn visit_grid(&self, x: i32, y: i32, visitor: &mut Visitor) {
        if let Some(grid) = self.find_grid(x, y) {
            grid.accept(visitor);
        }
    }

    #[allow(dead_code)]
    pub fn place_region(&mut self, region: Region, coordinate: Coordinate) -> Result<(), ()> {
        let grid = self.get_grid(coordinate.x, coordinate.y);
        grid.add_region(region);

        Ok(())
    }
}
