/*
 * This is responsible for moving the player into another room in the
 * cave as per their directions.  If room_number is a null string,
 * then we'll prompt the user for the next room to go into.   Once
 * we've moved into the room, we'll check for things like bats, pits,
 * and so on.  This routine returns 1 if something occurs that kills
 * the player and 0 otherwise...
 */

use crate::interaction::get_natural_number;
use crate::map::{Room, WumpusMap};
use crate::play::game::ExitStatus;
use crate::play::magic::{is_magic_tunnel_for_some_reason, select_random_room};
use crate::play::messages::{jump, pit_kill, pit_survive, wump_kill};
use crate::play::Things;
use crate::{dprint, Args};
use anyhow::Result;
use rand::prelude::ThreadRng;
use rand::Rng;

pub fn move_player(
    room: Option<usize>,
    things: &mut Things,
    wumpus_map: &WumpusMap,
    rng: &mut ThreadRng,
    _conf: &Args,
) -> Result<ExitStatus> {
    fn get_valid_room(room: Option<usize>, wumpus_map: &WumpusMap) -> anyhow::Result<usize> {
        let mut room_number = room;
        let room_count = wumpus_map.len();

        loop {
            if let Some(room_number) = room_number {
                if room_number < room_count + 1 {
                    return Ok(room_number);
                } else {
                    println!("What?  The cave surely isn't quite that big!");
                }
            } else {
                println!("Sorry, but we're constrained to a semi-Euclidean cave!");
            }
            room_number = get_natural_number("To which room do you wish to move? ")?;
        }
    }
    fn actually_move_player(
        destination_room: &Room,
        things: &mut Things,
        wumpus_map: &WumpusMap,
        rng: &mut ThreadRng,
    ) -> Result<()> {
        let mut room = destination_room;
        if is_magic_tunnel_for_some_reason(room, wumpus_map)? {
            dprint!("Randomizing room\n");
            room = select_random_room(wumpus_map, rng)?;
            jump(room.number);
        }

        things.set_player_location(&room.key);
        dprint!(
            "moving to room {room:?}. Player location now {:?}\n",
            things.player
        );
        Ok(())
    }

    let mut just_moved_by_bats = false;
    let room_number = get_valid_room(room, wumpus_map)?;
    let player_room = wumpus_map.try_get_room(&things.player)?;
    let targ_room = wumpus_map.get_room_from_room_number(room_number)?;
    if !player_room.connects_to(targ_room, wumpus_map)? {
        println!("*Oof!*  (You hit the wall)");
        if rng.gen_range(0..6) == 1 {
            println!("Your colorful comments awaken the wumpus!");
            move_wump(things, wumpus_map, rng)?;
            if things.player_wumpus_collision() {
                wump_kill();
                return Ok(ExitStatus::NormalExit);
            }
        }
    }

    actually_move_player(targ_room, things, wumpus_map, rng)?;

    loop {
        if things.player_wumpus_collision() {
            wump_kill();
            return Ok(ExitStatus::NormalExit);
        }
        if things.player_pit_collision() {
            if rng.gen_range(0..12) < 2 {
                pit_survive();
                return Ok(ExitStatus::Continue);
            } else {
                pit_kill();
                return Ok(ExitStatus::NormalExit);
            }
        }
        if things.player_bat_collision() {
            println!(
                "*flap*  *flap*  *flap*  (humongous bats pick you up and move you{}!)",
                if just_moved_by_bats { " again" } else { "" }
            );
            let new_player_room = select_random_room(wumpus_map, rng)?;
            things.set_player_location(&new_player_room.key);
            just_moved_by_bats = true;
        } else {
            return Ok(ExitStatus::Continue);
        }
    }
}

pub(crate) fn move_wump(
    things: &mut Things,
    wumpus_map: &WumpusMap,
    rng: &mut ThreadRng,
) -> anyhow::Result<()> {
    let wumpus = things.wump_room(wumpus_map)?;
    let wump_vertex = wumpus.get_vertex(wumpus_map)?;
    let neighbors = wump_vertex.neighbors();
    let neighbor_count = neighbors.len();
    let move_to = neighbors[rng.gen_range(0..neighbor_count)];
    things.set_wumpus_location(&move_to, wumpus_map)?;
    Ok(())
}