use crate::api::dungeon_mode::*;
use crate::api::enums::TerrainType;
use crate::ffi;
use core::slice;
impl DungeonTile {
    pub fn init(&mut self) {
        unsafe { ffi::InitializeTile(self as *mut _) };
    }
    pub fn get_terrain(&self) -> Option<TerrainType> {
        unsafe { ffi::GetTileTerrain(force_mut_ptr!(self)) }
            .try_into()
            .ok()
    }
    pub fn set_terrain_obstacle_checked(&mut self, secondary_terrain: bool, room_index: u8) {
        unsafe {
            ffi::SetTerrainObstacleChecked(
                self as *mut _,
                secondary_terrain as ffi::bool_,
                room_index,
            )
        }
    }
    pub fn set_secondary_terrain_on_wall(&mut self) {
        unsafe { ffi::SetSecondaryTerrainOnWall(self as *mut _) }
    }
    pub fn bind_trap(&mut self, trap: &mut DungeonEntity, is_visible: bool) {
        unsafe { ffi::BindTrapToTile(self, trap, is_visible as ffi::bool_) }
    }
}
pub trait DungeonTileGridRead<const W: usize, const H: usize> {
    fn get(&self, x: usize, y: usize) -> Option<&DungeonTile>;
    fn iter(&self) -> DungeonTileGridIter<W>;
}
pub trait DungeonTileGridWrite<const W: usize, const H: usize>: DungeonTileGridRead<W, H> {
    fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut DungeonTile>;
    fn insert(&mut self, x: usize, y: usize, tile: DungeonTile);
    fn iter_mut(&mut self) -> DungeonTileGridIterMut<W>;
}
pub struct DungeonTileGridRef<'a, const W: usize, const H: usize>(
    pub(crate) &'a [[*mut ffi::tile; W]; H],
);
pub struct DungeonTileGridMut<'a, const W: usize, const H: usize>(
    pub(crate) &'a mut [[*mut ffi::tile; W]; H],
);
impl<'a, const W: usize, const H: usize> DungeonTileGridRead<W, H>
    for DungeonTileGridRef<'a, W, H>
{
    fn get(&self, x: usize, y: usize) -> Option<&DungeonTile> {
        unsafe { self.0[y][x].as_ref() }
    }
    fn iter(&self) -> DungeonTileGridIter<W> {
        DungeonTileGridIter {
            tiles_iter: self.0.iter(),
            cur_row: None,
            cur_row_iter: None,
        }
    }
}
impl<'a, const W: usize, const H: usize> DungeonTileGridRead<W, H>
    for DungeonTileGridMut<'a, W, H>
{
    fn get(&self, x: usize, y: usize) -> Option<&DungeonTile> {
        unsafe { self.0[y][x].as_ref() }
    }
    fn iter(&self) -> DungeonTileGridIter<W> {
        DungeonTileGridIter {
            tiles_iter: self.0.iter(),
            cur_row: None,
            cur_row_iter: None,
        }
    }
}
impl<'a, const W: usize, const H: usize> DungeonTileGridWrite<W, H>
    for DungeonTileGridMut<'a, W, H>
{
    fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut DungeonTile> {
        unsafe { self.0[y][x].as_mut().map(|tile| &mut *tile) }
    }
    fn insert(&mut self, x: usize, y: usize, tile: DungeonTile) {
        let mut otile = unsafe { &mut *self.0[y][x] };
        otile._bitfield_align_1 = tile._bitfield_align_1;
        otile._bitfield_1 = tile._bitfield_1;
        otile.spawn_or_visibility_flags = tile.spawn_or_visibility_flags;
        otile.texture_id = tile.texture_id;
        otile.field_0x6 = tile.field_0x6;
        otile.room = tile.room;
        otile.walkable_neighbor_flags = tile.walkable_neighbor_flags;
        otile.monster = tile.monster;
        otile.object = tile.object;
    }
    fn iter_mut(&mut self) -> DungeonTileGridIterMut<W> {
        DungeonTileGridIterMut {
            tiles_iter: self.0.iter(),
            cur_row: None,
            cur_row_iter: None,
        }
    }
    }
pub struct DungeonTileGridIter<'a, const W: usize> {
    tiles_iter: slice::Iter<'a, [*mut ffi::tile; W]>,
    cur_row: Option<&'a [*mut ffi::tile; W]>,
    cur_row_iter: Option<slice::Iter<'a, *mut ffi::tile>>,
}
impl<'a, const W: usize> Iterator for DungeonTileGridIter<'a, W> {
    type Item = &'a DungeonTile;
    fn next(&mut self) -> Option<Self::Item> {
        if self.cur_row.is_none() {
            self.cur_row = self.tiles_iter.next();
            self.cur_row_iter = Some(self.cur_row?.iter());
        }
        let tile = self.cur_row_iter.as_mut().unwrap().next();
        match tile {
            Some(tile) => Some(unsafe { &**tile }),
            None => {
                self.cur_row = None;
                self.next()
            }
        }
    }
}
pub struct DungeonTileGridIterMut<'a, const W: usize> {
    tiles_iter: slice::Iter<'a, [*mut ffi::tile; W]>,
    cur_row: Option<&'a [*mut ffi::tile; W]>,
    cur_row_iter: Option<slice::Iter<'a, *mut ffi::tile>>,
}
impl<'a, const W: usize> Iterator for DungeonTileGridIterMut<'a, W> {
    type Item = &'a mut DungeonTile;
    fn next(&mut self) -> Option<Self::Item> {
        if self.cur_row.is_none() {
            self.cur_row = self.tiles_iter.next();
            self.cur_row_iter = Some(self.cur_row?.iter());
        }
        let tile = self.cur_row_iter.as_mut().unwrap().next();
        match tile {
            Some(tile) => Some(unsafe { &mut **tile }),
            None => {
                self.cur_row = None;
                self.next()
            }
        }
    }
}