use crate::cli_utils::*;
use crate::file_utils;
use crate::json_printer::BracketType;
use crate::sbx_specs::{ver_to_block_size, ver_to_data_size, ver_to_usize, ver_uses_rs};
use clap::*;
use std::str::FromStr;
pub fn sub_command<'a, 'b>() -> App<'a, 'b> {
    SubCommand::with_name("calc")
        .about("Calculate and display detailed information given an encoding configuration")
        .arg(
            Arg::with_name("in_file_size")
                .value_name("INFILE-SIZE")
                .required(true)
                .index(1)
                .help("Input file size"),
        )
        .arg(sbx_version_arg())
        .arg(Arg::with_name("no_meta").long("no-meta").help(
            "Skip metadata block in the calculations. Metadata block is
never skipped for version 17, 18, 19.
This means this option has no effect for version 17, 18, 19.",
        ))
        .arg(rs_data_arg())
        .arg(rs_parity_arg())
        .arg(burst_arg())
        .arg(json_arg())
}
pub fn calc<'a>(matches: &ArgMatches<'a>) -> i32 {
    let json_printer = get_json_printer!(matches);
    json_printer.print_open_bracket(None, BracketType::Curly);
    let (version, data_par_burst) = get_ver_and_data_par_burst_w_defaults!(matches, json_printer);
    let meta_enabled = Some(get_meta_enabled!(matches));
    let in_file_size = match u64::from_str(matches.value_of("in_file_size").unwrap()) {
        Ok(x) => x,
        Err(_) => exit_with_msg!(usr json_printer => "Invalid file size"),
    };
    let out_file_size = file_utils::from_orig_file_size::calc_container_size(
        version,
        meta_enabled,
        data_par_burst,
        in_file_size,
    );
    let total_block_count = file_utils::from_orig_file_size::calc_total_block_count_exc_burst_gaps(
        version,
        meta_enabled,
        data_par_burst,
        in_file_size,
    );
    let meta_block_count =
        file_utils::calc_meta_block_count_exc_burst_gaps(version, meta_enabled, data_par_burst);
    let (data_only_block_count, parity_block_count) =
        file_utils::from_orig_file_size::calc_data_only_and_parity_block_count_exc_burst_gaps(
            version,
            data_par_burst,
            in_file_size,
        );
    json_printer.print_open_bracket(Some("stats"), BracketType::Curly);
    print_if!(not_json => json_printer =>
             "SBX container general info";
             "========================================";);
    if ver_uses_rs(version) {
        print_maybe_json!(
            json_printer,
            "    SBX container version        : {}",
            ver_to_usize(version)
        );
    } else {
        print_maybe_json!(
            json_printer,
            "    SBX container version        : {}",
            ver_to_usize(version)
        );
    }
    print_maybe_json!(
        json_printer,
        "    SBX container block size     : {}",
        ver_to_block_size(version)
    );
    print_maybe_json!(
        json_printer,
        "    SBX container data  size     : {}",
        ver_to_data_size(version)
    );
    print_if!(not_json => json_printer => "";);
    print_if!(not_json => json_printer => "SBX block distribution";);
    print_if!(not_json => json_printer => "========================================";);
    if ver_uses_rs(version) {
        print_maybe_json!(
            json_printer,
            "    Metadata    block count      : {}",
            meta_block_count
        );
        print_maybe_json!(
            json_printer,
            "    Data only   block count      : {}",
            data_only_block_count
        );
        print_maybe_json!(
            json_printer,
            "    Data parity block count      : {}",
            parity_block_count
        );
        print_maybe_json!(
            json_printer,
            "    Total       block count      : {}",
            total_block_count
        );
    } else {
        print_maybe_json!(
            json_printer,
            "    Metadata block count         : {}",
            meta_block_count
        );
        print_maybe_json!(
            json_printer,
            "    Data     block count         : {}",
            data_only_block_count
        );
        print_maybe_json!(
            json_printer,
            "    Total    block count         : {}",
            total_block_count
        );
    }
    print_if!(not_json => json_printer => "";);
    print_if!(not_json => json_printer => "Error correction info";);
    print_if!(not_json => json_printer => "========================================";);
    if ver_uses_rs(version) {
        print_maybe_json!(
            json_printer,
            "    RS data   shard count        : {}",
            data_par_burst.unwrap().0
        );
        print_maybe_json!(
            json_printer,
            "    RS parity shard count        : {}",
            data_par_burst.unwrap().1
        );
        print_maybe_json!(
            json_printer,
            "    Burst error resistance level : {}",
            data_par_burst.unwrap().2
        );
    } else {
        print_maybe_json!(
            json_printer,
            "    RS data   shard count        : {}",
            null_if_json_else!(json_printer, "version does not use RS")
        );
        print_maybe_json!(
            json_printer,
            "    RS parity shard count        : {}",
            null_if_json_else!(json_printer, "version does not use RS")
        );
        print_maybe_json!(
            json_printer,
            "    Burst error resistance level : {}",
            null_if_json_else!(
                json_printer,
                "version does not support burst error resistance"
            )
        );
    }
    print_if!(not_json => json_printer => "";);
    if ver_uses_rs(version) && !json_printer.json_enabled() {
        let (data, par, burst) = data_par_burst.unwrap();
        let block_size = ver_to_block_size(version);
        println!("Error correction parameters interpretation");
        println!("========================================");
        if burst == 0 {
            print_block!(
                "    The container can tolerate {} SBX block corruptions", par;
                "    in each block set.";
                "";
                "    A block set consists of {} blocks ({} bytes).", (data + par), (data + par) * block_size;
                "";
                "    In total, {} blocks({} bytes) may be corrupted in", par, par * block_size;
                "    each block set.";
            );
        } else {
            if burst == 1 {
                print_block!(
                    "    Warning :";
                    "";
                    "        Burst error resistance level of {} may not provide", burst;
                    "        meaningful resistance.";
                    "";
                );
            }
            let block_set_size = data + par;
            let super_block_set_size = (data + par) * burst;
            print_block!("    The container can tolerate {} burst SBX block corruptions in", par;
                         "    each super block set ({} interleaved block sets).", burst;
                         "";
                         "    A block set consists of {} blocks ({} bytes).", block_set_size, block_set_size * block_size;
                         "";
                         "    A super block set consists of {} blocks ({} bytes).", super_block_set_size, super_block_set_size * block_size;
                         "";
                         "    Each burst error may be up to {} blocks ({} bytes) in size.", burst, burst * block_size;
                         "";
                         "    In total, {} sets of {} consecutive blocks ({} bytes) may be", par, burst, burst * block_size;
                         "    corrupted in each super block set.";
                         "";
                         "    The sets of corrupted blocks may be connected as well, so the";
                         "    largest single burst error that can be tolerated in each super";
                         "    block set is {} blocks ({} bytes) in size.", par * burst, par * burst * block_size;
                         "";
                         "    Note that the actual tolerance depends on the behaviour of";
                         "    the file system.";
            );
        }
        println!();
    }
    print_if!(not_json => json_printer => "File and container size";);
    print_if!(not_json => json_printer => "========================================";);
    print_maybe_json!(
        json_printer,
        "    File size                    : {}",
        in_file_size
    );
    print_maybe_json!(
        json_printer,
        "    SBX container size           : {}",
        out_file_size
    );
    json_printer.print_close_bracket();
    exit_with_msg!(ok json_printer => "")
}