use crate::common::serverstate::ClientId;
// TODO: room passwords?

#[derive(Copy, Clone)]
pub struct ClientArea<const N: usize> {
    clients: [Option<ClientId>; N],
}

impl<const N: usize> ClientArea<{N}> {
    pub fn new() -> ClientArea<{N}> {
        let mut clients: [std::mem::MaybeUninit<Option<ClientId>>; N] = unsafe {
            std::mem::MaybeUninit::uninit().assume_init()
        };
        for i in clients.iter_mut() {
            i.write(None);
        }

        ClientArea {
            clients: unsafe { (&clients as *const _ as *const [Option<ClientId>; N]).read()}
        }
    }
    
    
    fn add(&mut self, id: ClientId) -> Option<usize> {
        for (i, client) in self.clients.iter_mut().enumerate() {
            if client.is_none() {
                *client = Some(id);
                return Some(i);
            }
        }
        return None;
    }
    fn remove(&mut self, id: ClientId) {
        for client in self.clients.iter_mut() {
            if *client == Some(id) {
                *client = None
            }
        }
    }
}

pub type Lobby = ClientArea<12>;
pub type Room = ClientArea<4>;

pub type LobbyId = usize;
pub type RoomId = usize;

pub struct ClientLocation {
    lobbies: [Lobby; 15],
    rooms: [Option<Room>; 128],
}


enum JoinRoomError {
    RoomDoesNotExist,
    RoomFull,
}

enum JoinLobbyError {
    LobbyDoesNotExist,
    LobbyFull,
}

impl ClientLocation {
    pub fn new() -> ClientLocation {
        ClientLocation {
            lobbies: [Lobby::new(); 15],
            rooms: [None; 128],
        }
    }

    pub fn add_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<usize, JoinLobbyError> {
        self.lobbies.get_mut(lobby)
            .ok_or(JoinLobbyError::LobbyDoesNotExist)?
            .add(id)
            .ok_or(JoinLobbyError::LobbyFull)
    }

    pub fn add_to_room(&mut self, id: ClientId, room: RoomId) -> Result<usize, JoinRoomError> {
        self.rooms.get_mut(room)
            .ok_or(JoinRoomError::RoomDoesNotExist)?
            .as_mut()
            .ok_or(JoinRoomError::RoomDoesNotExist)?
            .add(id)
            .ok_or(JoinRoomError::RoomFull)
    }

    pub fn get_client_neighbors(&self, id: ClientId) -> Vec<ClientId> {
        for lobby in self.lobbies.iter() {
            if lobby.clients.contains(&Some(id)) {
                return lobby.clients.iter().filter(|c| c.is_some()).map(|c| c.unwrap()).collect();
            }
        }

        for room in self.rooms.iter() {
            if let Some(room) = room {
                if room.clients.contains(&Some(id)) {
                    return room.clients.iter().filter(|c| c.is_some()).map(|c| c.unwrap()).collect();
                }
            }
        }
        
        panic!()
    }
}