use prettytable::{cell, table, Table};

use std::io;
use std::io::Write;
use std::str::FromStr;

const CROSS: &'static str = "X";
const EMPTY: &'static str = " ";
const ROUND: &'static str = "O";

fn main() {
    let mut table = table![
        [EMPTY, EMPTY, EMPTY],
        [EMPTY, EMPTY, EMPTY],
        [EMPTY, EMPTY, EMPTY]
    ];
    let mut height = table.print_tty(false).unwrap();
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    let mut current = CROSS;
    let mut terminal = term::stdout().unwrap();
    loop {
        let mut line = String::new();
        print!("{} plays > ", current);
        height += 1;
        stdout.flush().unwrap();
        stdin.read_line(&mut line).expect("Cannot read input");
        let i = match usize::from_str(line.trim()) {
            Ok(i) => i,
            _ => {
                println!("Bad input");
                height += 1;
                continue;
            }
        };
        if i < 1 || i > 9 {
            println!("Bad input, should be between 1 and 9");
            height += 1;
            continue;
        }
        let x = (i - 1) % 3;
        let y = (i - 1) / 3;
        {
            let row = table.get_mut_row(y).unwrap();
            if row.get_cell(x).unwrap().to_string() != EMPTY {
                println!("There's already someone there");
                height += 1;
                continue;
            }
            row.set_cell(cell!(current), x).unwrap();
        }
        for _ in 0..height {
            terminal.cursor_up().unwrap();
            terminal.delete_line().unwrap();
        }
        height = table.print_tty(false).unwrap();
        if check(&table) {
            return;
        }
        if current == CROSS {
            current = ROUND;
        } else {
            current = CROSS;
        }
    }
}

fn get(table: &Table, x: usize, y: usize) -> String {
    match table.get_row(y) {
        Some(r) => match r.get_cell(x) {
            Some(c) => c.to_string(),
            _ => EMPTY.to_string(),
        },
        _ => EMPTY.to_string(),
    }
}

fn is(table: &Table, s: &str, x: usize, y: usize) -> bool {
    get(table, x, y).as_str() == s
}

fn check(table: &Table) -> bool {
    let mut full = true;
    for y in 0..3 {
        for x in 0..3 {
            if is(table, EMPTY, x, y) {
                full = false;
                continue;
            }
            let current = get(table, x, y);
            let c = current.as_str();
            if is(table, c, x + 1, y) && is(table, c, x + 2, y)
                || is(table, c, x + 1, y + 1) && is(table, c, x + 2, y + 2)
                || x >= 2 && is(table, c, x - 1, y + 1) && is(table, c, x - 2, y + 2)
                || is(table, c, x, y + 1) && is(table, c, x, y + 2)
            {
                println!("Game is over. {} is the winner", current);
                return true;
            }
        }
    }
    if full {
        println!("Game is over. It's a draw");
    }
    full
}
