use rand::prelude::*;
use std::{io, io::prelude::*, thread, time::Duration};

const COLORS: [&str; 5] = [
    "\x1b[42m", // Green background
    "\x1b[41m", // Red background
    "\x1b[43m", // Yellow background
    "\x1b[44m", // Blue background
    "\x1b[47m", // White background
];

const RESET: &str = "\x1b[0m";

const SQUARES: [[&str; 3]; 4] = [
    ["     ", "  1  ", "     "],
    ["     ", "  2  ", "     "],
    ["     ", "  3  ", "     "],
    ["     ", "  4  ", "     "],
];

fn main() {
    let mut turns: Vec<i8> = gen_turn_array();
    let mut amnt: i8 = 0;
    let mut good_answer: bool = true;
    let mut playing: bool = true;
    let mut high_score: i8 = 0;

    show_base("Press enter to start the game".to_string());
    let _ = io::stdin().read(&mut [0u8]).unwrap();

    while playing {
        while good_answer {
            show_game(amnt, turns.clone());
            amnt += 1;

            let mut response: String = String::new();
            print!("Enter the sequence: ");
            let _ = io::stdout().flush();
            io::stdin()
                .read_line(&mut response)
                .expect("Failed to read from stdin");

            if response.trim() == "q" {
                println!("Goodbye!");
                return;
            } else {
                if process_response(amnt, turns.clone(), response) {
                    println!("Good job! Now we add a turn.");
                    println!("Press enter to continue...");
                    let _ = io::stdin().read(&mut [0u8]).unwrap();
                } else {
                    if high_score < amnt {
                        high_score = amnt;
                    }
                    println!("Sorry mate, wrong sequence.");
                    println!("Turns: {}", amnt);
                    println!("High score: {}", high_score);
                    good_answer = false;
                }
            }
        }
        print!("Wanna play again? (y/n) ");
        let mut response: String = String::new();
        let _ = io::stdout().flush();
        io::stdin()
            .read_line(&mut response)
            .expect("Failure reading input.");
        if response.trim() == "y" {
            playing = true;
            good_answer = true;
            amnt = 0;
            turns = gen_turn_array();
        } else {
            playing = false;
        }
    }
}

fn gen_turn_array() -> Vec<i8> {
    let mut rng = rand::thread_rng();
    let mut vec = Vec::with_capacity(255);

    for _ in 0..255 {
        vec.push(rng.gen_range(1..5));
    }

    return vec;
}

fn process_response(amnt: i8, turns: Vec<i8>, response: String) -> bool {
    for i in 0..amnt {
        if let Some(ch) = response.chars().nth(i as usize) {
            if let Some(num) = ch.to_digit(10) {
                let value_as_i8 = num as i8;
                if turns[i as usize] != value_as_i8 {
                    return false;
                }
            } else {
                println!("The char #{} is not a digit.", i);
                return false;
            }
        } else {
            return false;
        }
    }
    return true;
}

fn show_game(amnt: i8, turns: Vec<i8>) {
    for i in 0..amnt + 1 {
        print!("{esc}[2J{esc}[1;1H", esc = 27 as char); //Clear the screen
        println!("============== Simon Game ==============");
        let turn: i8 = turns[i as usize];
        match turn {
            1 => {
                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[4],
                    SQUARES[0][0],
                    RESET,
                    COLORS[1],
                    SQUARES[1][0],
                    RESET,
                    COLORS[2],
                    SQUARES[2][0],
                    RESET,
                    COLORS[3],
                    SQUARES[3][0],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[4],
                    SQUARES[0][1],
                    RESET,
                    COLORS[1],
                    SQUARES[1][1],
                    RESET,
                    COLORS[2],
                    SQUARES[2][1],
                    RESET,
                    COLORS[3],
                    SQUARES[3][1],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[4],
                    SQUARES[0][2],
                    RESET,
                    COLORS[1],
                    SQUARES[1][2],
                    RESET,
                    COLORS[2],
                    SQUARES[2][2],
                    RESET,
                    COLORS[3],
                    SQUARES[3][2],
                    RESET
                );
            }
            2 => {
                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][0],
                    RESET,
                    COLORS[4],
                    SQUARES[1][0],
                    RESET,
                    COLORS[2],
                    SQUARES[2][0],
                    RESET,
                    COLORS[3],
                    SQUARES[3][0],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][1],
                    RESET,
                    COLORS[4],
                    SQUARES[1][1],
                    RESET,
                    COLORS[2],
                    SQUARES[2][1],
                    RESET,
                    COLORS[3],
                    SQUARES[3][1],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][2],
                    RESET,
                    COLORS[4],
                    SQUARES[1][2],
                    RESET,
                    COLORS[2],
                    SQUARES[2][2],
                    RESET,
                    COLORS[3],
                    SQUARES[3][2],
                    RESET
                );
            }
            3 => {
                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][0],
                    RESET,
                    COLORS[1],
                    SQUARES[1][0],
                    RESET,
                    COLORS[4],
                    SQUARES[2][0],
                    RESET,
                    COLORS[3],
                    SQUARES[3][0],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][1],
                    RESET,
                    COLORS[1],
                    SQUARES[1][1],
                    RESET,
                    COLORS[4],
                    SQUARES[2][1],
                    RESET,
                    COLORS[3],
                    SQUARES[3][1],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][2],
                    RESET,
                    COLORS[1],
                    SQUARES[1][2],
                    RESET,
                    COLORS[4],
                    SQUARES[2][2],
                    RESET,
                    COLORS[3],
                    SQUARES[3][2],
                    RESET
                );
            }
            4 => {
                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][0],
                    RESET,
                    COLORS[1],
                    SQUARES[1][0],
                    RESET,
                    COLORS[2],
                    SQUARES[2][0],
                    RESET,
                    COLORS[4],
                    SQUARES[3][0],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][1],
                    RESET,
                    COLORS[1],
                    SQUARES[1][1],
                    RESET,
                    COLORS[2],
                    SQUARES[2][1],
                    RESET,
                    COLORS[4],
                    SQUARES[3][1],
                    RESET
                );

                println!(
                    "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
                    COLORS[0],
                    SQUARES[0][2],
                    RESET,
                    COLORS[1],
                    SQUARES[1][2],
                    RESET,
                    COLORS[2],
                    SQUARES[2][2],
                    RESET,
                    COLORS[4],
                    SQUARES[3][2],
                    RESET
                );
            }
            _ => println!("Error parsing turn."),
        }
        println!("========================================");
        println!("Animating...");
        thread::sleep(Duration::from_millis(1000));
    }
    show_base("Animation finished!".to_string());
}

fn show_base(message: String) {
    print!("{esc}[2J{esc}[1;1H", esc = 27 as char); //Clear the screen
    println!("============== Simon Game ==============");
    println!(
        "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
        COLORS[0],
        SQUARES[0][0],
        RESET,
        COLORS[1],
        SQUARES[1][0],
        RESET,
        COLORS[2],
        SQUARES[2][0],
        RESET,
        COLORS[3],
        SQUARES[3][0],
        RESET
    );

    println!(
        "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
        COLORS[0],
        SQUARES[0][1],
        RESET,
        COLORS[1],
        SQUARES[1][1],
        RESET,
        COLORS[2],
        SQUARES[2][1],
        RESET,
        COLORS[3],
        SQUARES[3][1],
        RESET
    );

    println!(
        "{}{}{}|{}{}{}|{}{}{}|{}{}{}",
        COLORS[0],
        SQUARES[0][2],
        RESET,
        COLORS[1],
        SQUARES[1][2],
        RESET,
        COLORS[2],
        SQUARES[2][2],
        RESET,
        COLORS[3],
        SQUARES[3][2],
        RESET
    );
    println!("========================================");
    println!("{}", message);
}