use player::PlayerId;
use thiserror::Error;

mod bag;
mod board;
mod data;
mod dictionary;
mod player;

#[derive(Debug, Error)]
pub enum GameError {
    #[error("Not enough tiles in bag")]
    NotEnoughTiles(#[from] bag::BagError),
}

#[derive(Debug)]
pub struct Game {
    board: board::Board,
    bag: bag::Bag,
    players: Vec<player::Player>,
}

impl Game {
    pub fn new() -> Game {
        let bag_data = data::BagData::load_from_json(include_str!("../bag.json")).unwrap();
        let bag = bag::Bag::from(bag_data);
        let dictionary = dictionary::Dictionary::new();
        Game {
            board: board::Board::new(dictionary),
            bag,
            players: Vec::new(),
        }
    }

    pub fn players(&self) -> &Vec<player::Player> {
        &self.players
    }

    pub fn bag(&self) -> &bag::Bag {
        &self.bag
    }

    pub fn board(&self) -> &board::Board {
        &self.board
    }

    pub fn add_player(&mut self) -> Result<PlayerId, GameError> {
        let id = PlayerId(self.players.len() as u32);
        self.players
            .push(player::Player::new(id, self.bag.take(7)?));
        Ok(id)
    }

    pub fn add_players(&mut self, count: usize) -> Result<Vec<PlayerId>, GameError> {
        let mut ids = Vec::new();
        for _ in 0..count {
            ids.push(self.add_player()?);
        }
        Ok(ids)
    }
}

impl Default for Game {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use crate::Game;

    #[test]
    fn test_game() {
        let mut game = Game::new();
        game.add_players(2).unwrap();
        for player in game.players() {
            println!("Player bag: {}", player.bag());
        }
        println!("Game bag: {}", game.bag());
        println!("Game board: {}", game.board());
    }
}
