use rand_core::OsRng;
use roll_rs::{roll_direction, roll_inline, roll_stats, Parser};
use std::{env, process};

fn main() {
    if env::args().len() <= 1 {
        print_usage()
    }

    let mut argv: Vec<String> = env::args().skip(1).collect();
    match argv[0].as_str() {
        "stats" => print_roll_stats(),
        "dir" => print_roll_dir(),
        "-h" | "--help" | "" => print_usage(),
        _ => {
            let mut advanced = false;
            let mut short = false;

            argv.retain(|x| match x.as_str() {
                "-a" | "--advanced" => {
                    advanced = true;
                    false
                }
                "-s" | "--short" => {
                    short = true;
                    false
                }
                _ => true,
            });

            if short {
                roll_short(&argv.join(" "), advanced)
            } else {
                roll_long(&argv.join(" "), advanced);
            }
        }
    }
}

fn print_usage() -> ! {
    println!("Syntax is: roll <dice_code>\nExample: roll 2d8 + 6 + d8");
    println!("Instead of a dice code you can also put \"stats\" or \"dir\" for a stats roll or direction roll respectively");
    println!("\nArgs: ");
    println!("  -a: advanced mode (composite dice notation)");
    println!("  -s: smaller output");
    process::exit(0)
}

fn print_roll_dir() {
    let dir = roll_direction(OsRng);
    println!("{}", dir);
}

fn roll_short(s: &str, advanced: bool) {
    match roll_inline(s, advanced) {
        Ok(s) => println!("{}", s),
        Err(e) => {
            eprintln!("{}", e);
            process::exit(2);
        }
    }
}

fn roll_long(s: &str, advanced: bool) {
    let mut p = Parser::new(s);
    p.advanced = advanced;

    let ast = match p.parse() {
        Ok(i) => i,
        Err(e) => {
            eprintln!("{}", e);
            process::exit(1)
        }
    };

    let mut rolls = Vec::new();
    let total = match ast.interp(&mut rolls) {
        Ok(i) => i,
        Err(e) => {
            eprintln!("{}", e);
            process::exit(2)
        }
    };

    rolls.sort_by_key(|i| i.0);

    let mut header = String::new();
    for (x, roll) in &rolls {
        for _ in header.len()..(*x as usize) {
            header.push(' ');
        }
        header.push_str(&format!("d{}", roll.sides))
    }

    println!("{}", header);
    println!("{} = {}", s, total);

    let mut rows = Vec::new();

    for (x, roll) in rolls {
        while roll.vals.len() > rows.len() {
            rows.push(String::new());
        }

        for (index, val) in roll.vals.iter().enumerate() {
            for _ in rows[index].len()..(x as usize) {
                rows[index].push(' ');
            }

            rows[index].push_str(&format!("{}", val))
        }
    }

    for row in rows {
        println!("{}", row);
    }
}

fn print_roll_stats() {
    print!("{}", roll_stats())
}
