use crate::interaction::{get_command, parse_natural_numbers};
use crate::map::WumpusMap;
use crate::play::movement::move_player;
use crate::play::shoot;
use crate::play::things::Things;
use crate::Args;
use anyhow::Result;
use rand::Rng;

pub(crate) enum ExitStatus {
    NormalExit,
    ImmediateExit,
    Continue,
}

pub(crate) fn play(wumpus_map: &WumpusMap, config: &Args, mut things: Things) -> Result<()> {
    let mut arrows = config.arrows;
    let mut rng = rand::thread_rng();

    loop {
        let room = wumpus_map.try_get_room(&things.player)?;
        room.display_stats(arrows, &things, wumpus_map, config)?;
        let (command, parameters) = get_command("Move or shoot? (m-s) ");
        let exit_status = match command.unwrap_or_default().as_str() {
            "m" => {
                let room = if let Some(parameters) = parameters {
                    Some(parameters.parse::<usize>()?)
                } else {
                    None
                };
                move_player(room, &mut things, &wumpus_map, &mut rng, config)?
            }
            "s" => {
                let rooms = parse_natural_numbers(parameters.unwrap_or_default())?;
                shoot::shoot_arrow(
                    rooms,
                    &mut things,
                    &mut arrows,
                    &wumpus_map,
                    &mut rng,
                    config,
                )?
            }
            "q" | "x" => ExitStatus::ImmediateExit,
            "" => ExitStatus::NormalExit,
            _ => {
                if rng.gen_range(0..14) == 1 {
                    println!("¿Que pasa?");
                } else {
                    println!("I don't understand!");
                }
                ExitStatus::Continue
            }
        };

        // In the original C version of the game, Normal Exist & Immediate Exit did
        // different things. "Normal Exit" stays in the "Care to play another game?"
        // . "Immediate Exit" literally exits, immediately.
        // One way to implement this would be to have an error variant that is returned
        // on Immediate Exit & is handled by the caller.
        match exit_status {
            ExitStatus::ImmediateExit => {
                return Ok(());
            }
            ExitStatus::NormalExit => {
                return Ok(());
            }
            ExitStatus::Continue => {}
        }
    }
}