use epkard::*;

#[derive(Serialize, Deserialize)]
struct State {
    has_key: bool,
}

#[derive(Clone, Serialize, Deserialize)]
enum Room {
    Start,
    Side,
    Exit,
}

impl Node for Room {
    type State = State;
    fn next<F>(self, rt: &mut Runtime<Self, F>) -> Control<Self>
    where
        F: Frontend,
    {
        rt.clear();
        match self {
            Room::Start => {
                rt.println("You are in a room.");
                rt.wait()?;
                let next_room = rt.multiple_choice_named(
                    "Where do you want to go?",
                    vec![
                        ("The door with light behind it", Room::Exit),
                        ("The door with no light behind it", Room::Side),
                    ],
                )?;
                next(next_room)
            }
            Room::Side => {
                rt.println(if rt.has_key {
                    "There is nothing here."
                } else {
                    "There is a key on the floor."
                });
                rt.wait()?;
                let pick_up_key = rt.multiple_choice_named(
                    "What do you want to do?",
                    Some(("Pick up the key", true))
                        .filter(|_| !rt.has_key)
                        .into_iter()
                        .chain(Some(("Go back through the door", false))),
                )?;
                if pick_up_key {
                    rt.has_key = true;
                    next(Room::Side)
                } else {
                    next(Room::Start)
                }
            }
            Room::Exit => {
                rt.println("There is another door with a bright light behind it.");
                rt.wait()?;
                let try_to_leave = rt.multiple_choice_named(
                    "Where do you want to go?",
                    vec![
                        ("Through the door ahead", true),
                        ("Back through the door you came in", false),
                    ],
                )?;
                if try_to_leave {
                    if rt.has_key {
                        rt.println("The door is locked. You unlock the door with your key.");
                        rt.wait()?;
                        rt.println("You escaped! You win!");
                        rt.wait()?;
                        exit()
                    } else {
                        rt.println("The door is locked.");
                        rt.wait()?;
                        next(Room::Exit)
                    }
                } else {
                    next(Room::Start)
                }
            }
        }
    }
}

#[derive(Clone)]
struct Inventory;

impl Node for Inventory {
    type State = State;
    fn next<F>(self, rt: &mut Runtime<Self, F>) -> Control<Self>
    where
        F: Frontend,
    {
        if rt.has_key {
            rt.println("You have a key");
        } else {
            rt.println("You have nothing");
        }
        exit()
    }
}

fn main() {
    run(
        Room::Start,
        &mut State { has_key: false },
        &mut CliFrontend::new(),
    )
    .command("save", |rt| {
        match rt.save("rooms.yaml") {
            Ok(_) => rt.println("Save complete"),
            Err(e) => rt.println(format!("Error: {}", e)),
        }
        Continue
    })
    .command("load", |rt| {
        match rt.load("rooms.yaml") {
            Ok(_) => rt.println("Load complete"),
            Err(e) => rt.println(format!("Error: {}", e)),
        }
        Skip
    })
    .command("inv, inventory", |rt| rt.push_node(Inventory));
}