extern crate ansi_term;
extern crate chrono;
extern crate clap;
extern crate personify;

use ansi_term::Color::{self, Green, White};
use clap::Clap;
use personify::PersonaData;
use std::convert::TryFrom;

#[derive(Clap, Debug)]
#[clap(name = "pfy", version)]
enum PfyOpt {
    /// Generate a Persona ID from the provided flags.
    Create {
        /// The model type of the unit.
        #[clap(long, default_value = "A")]
        model: char,
        /// Year of manufacture (last 3 digits)
        #[clap(long, default_value = "223")]
        year: u16,
        /// Month of manufacture (0-11)
        #[clap(long, default_value = "0")]
        month: u16,
        /// Personality identifier (2 characters, e.g. 'TI' or 'QZ')
        #[clap(long, default_value = "QZ")]
        personality: String,
        /// Manufacture number
        #[clap(long, default_value = "029")]
        manufacture_no: u16,
    },
    /// Parse a Persona ID for the info within.
    Parse { id: String },
}

fn err(msg: impl std::fmt::Display) -> ! {
    eprintln!(
        "{}{} {}",
        Color::Red.bold().paint("ERROR"),
        Color::White.bold().paint(":"),
        msg
    );
    std::process::exit(1);
}

fn main() {
    let opt = PfyOpt::parse();

    match opt {
        PfyOpt::Create {
            model,
            personality,
            month,
            year,
            manufacture_no,
        } => {
            let model = match personify::ModelType::try_from(model) {
                Ok(v) => v,
                Err(e) => err(format!("{}", e)),
            };

            let personality = {
                let p = personality.chars().collect::<Vec<_>>();
                if p.len() != 2 {
                    err(format!(
                        "Invalid personality specifier provided: {}",
                        personality
                    ))
                } else {
                    [p[0], p[1]]
                }
            };

            let date = chrono::NaiveDate::from_ymd((year + 2000) as i32, (month + 1) as u32, 1);

            let dat = PersonaData {
                model,
                personality,
                date,
                manufacture_no,
            };

            println!("{} {}", White.bold().paint("Generated Persona ID:"), dat);
        }
        PfyOpt::Parse { id } => match id.parse::<PersonaData>() {
            Ok(data) => {
                println!(
                    "{}",
                    Green.bold().paint(format!("Persona ID '{}' parsed!", id))
                );
                let style = White.bold();
                println!();

                println!("{} {}", style.paint("Model Type:      "), data.model);
                println!(
                    "{} {}{}",
                    style.paint("Personality:     "),
                    data.personality[0],
                    data.personality[1]
                );
                println!(
                    "{} {}",
                    style.paint("Manufacture Date:"),
                    data.date.format("%B %Y")
                );
                println!(
                    "{} {}",
                    style.paint("Manufacture #:   "),
                    data.manufacture_no
                );
            }
            Err(e) => println!("{:?}", e),
        },
    }
}
