const RAND_MIN: i32 = 0;
const RAND_MAX: i32 = 100;

enum Continue {
  Yes,
  No,
}

enum State {
  StartUp,
  InGame,
  GameOver,
}

struct Cli {
  state: State,
  hello_message: bool,
  nth_game: i32,
  nth_tries: i32,
  the_answer: i32,
}

impl Cli {
  fn new() -> Self {
    Cli {
      state: State::StartUp,
      hello_message: true,
      nth_game: 1,
      nth_tries: 0,
      the_answer: 0,
    }
  }
 
  fn run(&mut self) -> Continue {
    match self.state {
      State::StartUp => {
        self.start_up();
        return Continue::Yes;
      }
      State::InGame => {
        self.in_game();
        return Continue::Yes;
      },
      State::GameOver => {
        return self.game_over();
      }
    }
  }

  fn start_up(&mut self) {
    if self.hello_message {
      println!("Welcome to guessing game!");
      self.hello_message = false;
    }

    println!("Guess the number from {RAND_MIN} to {RAND_MAX}.");

    use rand::Rng; // trait!
    let mut rng = rand::thread_rng();
    let num = rng.gen_range(RAND_MIN..=RAND_MAX);

    assert!(num >= RAND_MIN && num <= RAND_MAX,
      "wtf? the rand gen is broken... why did it make {num}..."
    );

    self.the_answer = num;

    self.state = State::InGame;
  }

  fn in_game(&mut self) {
    let mut guess = String::new();

    print!("Your guess: ");
    use std::io::Write; // trait!
    std::io::stdout().flush().unwrap();

    std::io::stdin().read_line(&mut guess).unwrap();
    guess = guess.trim().to_string();

    let guess = match guess.parse::<i32>() {
      Ok(guess) => guess,
      Err(_) => {
        println!("Come on! Write the proper integer.");
        return;
      }
    };

    if guess < RAND_MIN || guess > RAND_MAX {
      println!("No, no. The range is from {RAND_MIN} to {RAND_MAX}.");
      return;
    }

    self.nth_tries += 1;

    if guess < self.the_answer {
      println!("lol. too low bruh. call higher.");
    } else if guess > self.the_answer {
      println!("chill man! call lower!");
    } else {
      println!("oh my gosh, man. you are correct. it's {}.", self.the_answer);
      println!("you tried {} times.", self.nth_tries);
      println!("one more game, bruh?");
      self.state = State::GameOver;
    }

  }

  fn game_over(&mut self) -> Continue {
   
    println!("> [Y]eah, bring it on.");
    println!("> [N]ah, I'm cool.");

    let mut input = String::new();

    std::io::stdin().read_line(&mut input).unwrap();
    input = input.trim().to_string();

    match input.as_ref() {
      "y" | "Y" => {
        self.nth_game += 1;
        self.nth_tries = 0;
        println!("Ok, here it goes. the round {}.", self.nth_game);
        self.state = State::StartUp;
      },
      "n" | "N" => {
        println!("Seriously? Okay, goodbye...");
        return Continue::No;        
      },
      _ => {
        println!("Come on! man! You've just beat the game,");
        println!("and now is pressing Y or N so damn hard??");
      }
    }

    return Continue::Yes;
  }
}

fn main() {
  let mut cli = Cli::new();

  loop {
    match cli.run() {
      Continue::Yes => continue,
      Continue::No => break,
    }
  }
}