use std::io::{stdout, Write};
use crate::{
gameboard::GameBoard,
player_type::PlayerType,
active_player::ActivePlayer,
ai::AiPlayer,
game_settings::{GameAutoquitMode, GameMode}
};
use crossterm::{
terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
QueueableCommand
};
mod event_handling;
mod game;
mod play_again_menu;
mod setup_menu;
use self::setup_menu::SetupMenu;
pub struct UI{
player_x: PlayerType,
player_o: PlayerType,
active_player: ActivePlayer,
cursor_x_pos: u8,
cursor_y_pos: u8,
game_board: GameBoard,
terminal_x_size: u16,
terminal_y_size: u16,
exit_flag: bool,
player_x_score: u32,
player_o_score: u32,
number_of_draws: u32,
game_autoquit_mode: GameAutoquitMode,
game_autoquit_value: u32,
game_mode: GameMode
}
impl UI{
const TERMSIZE_MIN_X: u16 = 11;
const TERMSIZE_MIN_Y: u16 = 8;
pub fn new() -> crossterm::Result<Self>
{
Self::setup_terminal()?;
let (terminal_x_size, terminal_y_size) = terminal::size()?;
let new_instance = Self{
player_x: PlayerType::Human,
player_o: PlayerType::AI(AiPlayer::default()),
active_player: ActivePlayer::PlayerX,
cursor_x_pos: 0,
cursor_y_pos: 0,
game_board: GameBoard::new(),
terminal_x_size,
terminal_y_size,
exit_flag: false,
player_x_score: 0,
player_o_score: 0,
number_of_draws: 0,
game_autoquit_mode: GameAutoquitMode::default(),
game_autoquit_value: 1,
game_mode: GameMode::default()
};
Ok(new_instance)
}
pub fn setup_menu(&mut self) -> crossterm::Result<()>
{
let mut setup_menu = SetupMenu::new();
if !setup_menu.setup_menu_loop()? {
self.exit_flag = true;
}
setup_menu.apply_settings(self);
Ok(())
}
pub fn autoquit_satisfied(&self) -> bool
{
match self.game_autoquit_mode {
GameAutoquitMode::Unlimited => false,
GameAutoquitMode::GameNumberLimit => {
self.number_of_games() >= self.game_autoquit_value
},
GameAutoquitMode::NonDrawNumberLimit => {
self.player_x_score() + self.player_o_score() >= self.game_autoquit_value
},
GameAutoquitMode::ScoreNumberLimit => {
self.player_x_score() >= self.game_autoquit_value ||
self.player_o_score() >= self.game_autoquit_value
}
}
}
pub fn borrow_game_board(&self) -> &GameBoard
{
&self.game_board
}
pub fn take_game_board(mut self) -> GameBoard
{
let game_board = std::mem::take(&mut self.game_board);
drop(self);
game_board
}
pub fn player_x(&self) -> &PlayerType
{
&self.player_x
}
pub fn player_o(&self) -> &PlayerType
{
&self.player_o
}
pub fn player_x_score(&self) -> u32
{
self.player_x_score
}
pub fn player_o_score(&self) -> u32
{
self.player_o_score
}
pub fn number_of_draws(&self) -> u32
{
self.number_of_draws
}
pub fn number_of_games(&self) -> u32
{
self.player_x_score + self.player_o_score + self.number_of_draws
}
pub fn active_player(&self) -> &ActivePlayer
{
&self.active_player
}
pub fn active_player_mut(&mut self) -> &mut ActivePlayer
{
&mut self.active_player
}
fn setup_terminal() -> crossterm::Result<()>
{
terminal::enable_raw_mode()?;
stdout()
.queue(EnterAlternateScreen)?
.flush()?;
Ok(())
}
fn cleanup_terminal() -> crossterm::Result<()>
{
stdout()
.queue(LeaveAlternateScreen)?
.flush()?;
terminal::disable_raw_mode()?;
Ok(())
}
fn reset_cursor_pos(&mut self)
{
self.cursor_x_pos = 1;
self.cursor_y_pos = 1;
}
fn active_player_type(&self) -> &PlayerType
{
match self.active_player{
ActivePlayer::PlayerO => &self.player_o,
ActivePlayer::PlayerX => &self.player_x
}
}
}
impl Drop for UI {
fn drop(&mut self)
{
if UI::cleanup_terminal().is_err(){
panic!("Failed to cleanup terminal when dropping UI");
}
}
}
impl Default for UI {
fn default() -> Self
{
match Self::new(){
Ok(instance) => instance,
Err(_) => panic!("failed to create default UI instance")
}
}
}