use std::io::{self, Write};
use std::process;

pub struct Game {
    encounter: Option<Encounter>,
    zone: Zone,
    adventurer: Adventurer,
    scarers: Vec<Scarer>,
}

impl Game {
    pub fn new() -> Game {
        Game {
            encounter: None,
            zone: Zone { width: 4, depth: 4 },
            adventurer: Adventurer {
                name: String::from("adv"),
                h: Humors {
                    health: 1000,
                    attack: 1,
                    critical_pct: 10,
                },
                l: Location { x: 0, y: 0 },
            },
            scarers: vec![Scarer {
                name: String::from("Blue Minotaur"),
                description: String::from("Towering half bull with flaming nostrils"),
                h: Humors {
                    health: 4000,
                    attack: 4,
                    critical_pct: 2,
                },
                l: Location { x: 2, y: 2 },
            }],
        }
    }

    pub fn play(&mut self) {
        loop {
            self.step();
        }
    }

    fn on(&mut self, event: Event) {
        match event {
            Event::ApproachEdge => {
                println!("// you stand at the edge of the zone");
            }
            Event::Encounter(s) => {
                println!("// !! you encounter an {}", s.name);
            }
            Event::Unencounter(s, o) => {
                println!("// !! you unencountered an {} with outcome {:?}", s.name, o);
            }
            Event::Fall => {
                println!("// you fall out of the zone");
                process::exit(0);
            }
            Event::Step(d) => {
                println!("// you step {:?}", d);
            }
            Event::Miss => {
                println!("// you brace yourself for the next step");
            }
        }
    }

    pub fn step(&mut self) {
        {
            let al = &self.adventurer.l;

            if al.x == 0
                || al.y == 0
                || al.x as u8 == self.zone.width
                || al.y as u8 == self.zone.depth
            {
                self.on(Event::ApproachEdge);
            }
        }

        let adventurer_loc = Location {
            x: self.adventurer.l.x,
            y: self.adventurer.l.y,
        };

        let evt: Event = Event::Miss;

        for s in self.scarers.iter() {
            if adventurer_loc == s.l {
                evt = Event::Encounter(*s);
            }
        }

        self.on(evt);

        print!("> ");
        io::stdout().flush().expect("failed to flush stdout");

        let mut line = String::new();
        io::stdin()
            .read_line(&mut line)
            .expect("failed to read line of input");
        line = line.trim().to_string();

        match &mut self.encounter {
            None => { mut self.wandering_step(&line) },
            Some(e) => { mut self.encounter_step(&line, e) },
        }
    }

    fn encounter_step(&self, line: &str, enc: &mut Encounter) {
        eprintln!(
            "// this is not an encounter {:?} from line {:?} OK",
            enc, line
        );
    }

    fn wandering_step(&mut self, line: &str) {
        let v: Vec<&str> = line.split(' ').collect();
        match v.get(0) {
            Some(cmd) => {
                match *cmd {
                    "help" => {
                        println!("// maybe try 'loc'");
                    }
                    "loc" => {
                        println!(
                            "// you stand at ({}, {})",
                            self.adventurer.l.x, self.adventurer.l.y
                        );
                    }
                    "step" => {
                        let (mv, dir) = match v.get(1) {
                            Some(direction) => {
                                let direction = direction.trim().to_lowercase();
                                match direction.as_str() {
                                    "forward" => ((1i8, 0i8), Direction::Forward),
                                    "back" => ((-1i8, 0i8), Direction::Back),
                                    "left" => ((0i8, 1i8), Direction::Left),
                                    "right" => ((0i8, -1i8), Direction::Right),
                                    _ => {
                                        println!("unknown direction {}", direction);
                                        ((0i8, 0i8), Direction::Forward)
                                    }
                                }
                            }
                            None => ((1i8, 0i8), Direction::Forward),
                        };

                        let loc = &mut self.adventurer.l;
                        loc.x += mv.0;
                        loc.y += mv.1;
                        println!("// you step {:?}", dir);
                    }
                    _ => {
                        println!("// what is this {}?", cmd);
                    }
                };
            }
            None => println!("// ..."),
        }
    }
}

#[derive(Debug)]
enum Direction {
    Forward,
    Back,
    Left,
    Right,
}

#[derive(Debug)]
enum EncounterOutcome {
    Victory,
    Defeat,
}

enum Event {
    Fall,
    ApproachEdge,
    Step(Direction),
    Encounter(Scarer),
    Unencounter(Scarer, EncounterOutcome),
    Miss,
}

#[derive(Debug)]
struct Encounter {
    scarer: Scarer,
}

#[derive(Debug)]
struct Humors {
    health: u16,
    attack: u8,
    critical_pct: u8,
}

#[derive(Debug)]
struct Location {
    x: i8,
    y: i8,
}

impl PartialEq for Location {
    fn eq(&self, other: &Self) -> bool {
        self.x == other.x && self.y == other.y
    }
}

struct Adventurer {
    name: String,
    h: Humors,
    l: Location,
}

#[derive(Debug)]
struct Scarer {
    name: String,
    description: String,
    h: Humors,
    l: Location,
}

struct Zone {
    width: u8,
    depth: u8,
}
