Documentation
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};

use xron::Error;

fn app() -> App<'static, 'static> {
    App::new("xron")
        .about("Converts XLSX files to RON")
        .author("Thomas Schaller")
        .setting(AppSettings::ArgsNegateSubcommands)
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(
            Arg::with_name("INPUT_FILE")
                .required(true)
                .takes_value(true)
                .value_name("INPUT_FILE")
                .index(1),
        )
        .arg(
            Arg::with_name("reverse")
                .help("Converts RON to XLSX")
                .short("r")
                .long("reverse")
                .requires("output-file"),
        )
        .arg(
            Arg::with_name("skip-empty")
                .help("Does not insert empty rows into XLSX")
                .long("skip-empty")
                .requires("reverse"),
        )
        .arg(
            Arg::with_name("column-width")
                .help("Sets column width for XLSX")
                .long("column-width")
                .requires("reverse")
                .takes_value(true)
                .value_name("COLUMN_WIDTH"),
        )
        .arg(
            Arg::with_name("output-file")
                .short("o")
                .long("output-file")
                .takes_value(true)
                .value_name("OUTPUT_TO_FILE"),
        )
        .subcommand(
            SubCommand::with_name("duplicates")
                .about("Prints duplicated line numbers")
                .arg(
                    Arg::with_name("INPUT_FILE")
                        .required(true)
                        .takes_value(true)
                        .value_name("INPUT_FILE")
                        .index(1),
                )
                .arg(
                    Arg::with_name("column")
                        .long("column")
                        .help("column number to search (zero-based)")
                        .default_value("0")
                        .takes_value(true)
                        .value_name("COLUMN_NUMBER"),
                )
                .arg(
                    Arg::with_name("sheet")
                        .long("sheet")
                        .help("sheet to search [default: first]")
                        .takes_value(true)
                        .value_name("SHEET_NAME"),
                )
                .arg(
                    Arg::with_name("write-unique-xlsx")
                        .long("write-unique-xlsx")
                        .help("Creates a copy without duplicates")
                        .takes_value(true)
                        .conflicts_with("sheet")
                        .value_name("NEW_UNIQUE_XLSX_FILE"),
                ),
        )
        .subcommand(
            SubCommand::with_name("intersect")
                .about("Only keep intersect rows")
                .arg(
                    Arg::with_name("BASE_FILE")
                        .help("Base file to remove rows from")
                        .required(true)
                        .takes_value(true)
                        .value_name("BASE_FILE")
                        .index(1),
                )
                .arg(
                    Arg::with_name("CMP_FILE")
                        .help("File to compare rows to")
                        .required(true)
                        .takes_value(true)
                        .value_name("CMP_FILE")
                        .index(2),
                )
                .arg(
                    Arg::with_name("output-file")
                        .short("o")
                        .long("output-file")
                        .takes_value(true)
                        .value_name("OUTPUT_TO_FILE"),
                ),
        )
}

fn core(matches: &ArgMatches<'_>) -> Result<(), Error> {
    let file = matches.value_of("INPUT_FILE").unwrap();

    if matches.is_present("reverse") {
        let skip_empty = matches.is_present("skip-empty");
        let column_width = matches.value_of("skip-empty").unwrap_or("60.0");
        let column_width = column_width.parse().unwrap_or(60.0);
        let output_file = matches.value_of("output-file").unwrap();

        let ron_workbook = xron::RonWorkbook::read_ron(file)?;
        ron_workbook.write_xlsx(output_file, column_width, skip_empty)?;
    } else {
        let ron_workbook = xron::RonWorkbook::read_xlsx(file)?;

        let serialized = ron::ser::to_string_pretty(&ron_workbook, Default::default())?;
        println!("{}", serialized);

        if let Some(file) = matches.value_of("output-file") {
            std::fs::write(file, serialized.as_bytes())?;
        }
    }

    Ok(())
}

fn duplicates(matches: &ArgMatches<'_>) -> Result<(), Error> {
    let file = matches.value_of("INPUT_FILE").unwrap();
    let column = matches.value_of("column").unwrap_or("0");
    let column = column.parse().unwrap_or(0usize);

    let mut ron_workbook = xron::RonWorkbook::read_xlsx(file)?;

    let sheet = {
        matches
            .value_of("sheet")
            .and_then(|name| ron_workbook.sheets.get(name))
    };
    let sheet = match sheet {
        Some(x) => x,
        None => ron_workbook.sheets.values().next().expect("No sheets"),
    };

    if let Some(unique_xlsx) = matches.value_of("NEW_UNIQUE_XLSX_FILE") {
        ron_workbook.retain_unique(column);
        ron_workbook.write_xlsx(unique_xlsx, 60.0, false)?;
    } else {
        let lines = sheet.find_duplicates(column);
        println!("Duplicated lines: {:?}", lines);
    }

    Ok(())
}

fn unique(matches: &ArgMatches<'_>) -> Result<(), Error> {
    let base_file = matches.value_of("BASE_FILE").unwrap();
    let cmp_file = matches.value_of("CMP_FILE").unwrap();

    let mut base_workbook = xron::RonWorkbook::read_xlsx(base_file)?;
    let cmp_workbook = xron::RonWorkbook::read_xlsx(cmp_file)?;

    base_workbook.retain_intersecting(&cmp_workbook);

    let serialized = ron::ser::to_string_pretty(&base_workbook, Default::default())?;
    println!("{}", serialized);

    if let Some(file) = matches.value_of("output-file") {
        std::fs::write(file, serialized.as_bytes())?;
    }

    Ok(())
}

fn run() -> Result<(), Error> {
    let app = app();
    let matches = app.get_matches();

    match matches.subcommand() {
        ("duplicates", Some(sm)) => duplicates(sm)?,
        ("intersect", Some(sm)) => unique(sm)?,
        _ => core(&matches)?,
    }

    Ok(())
}

fn main() {
    if let Err(e) = run() {
        eprintln!("Error: {}", e);
    }
}