pub mod sim;
mod stats;
mod types;

use std::{env, process};
use crate::types::{Quorum, Region};
use crate::stats::{CurrentStats, CumulativeStats};
use crate::sim::Simulation;
use rand_pcg::Pcg64;
use rand::SeedableRng;

fn usage(cmd: &String) {
    eprintln!("Usage: {} h m r k g iters seed", cmd);
    eprintln!("h:           number of honest nodes");
    eprintln!("m:           number of malicious nodes");
    eprintln!("lg_r:        log_2 of the number of regions");
    eprintln!("lg_c:        log_2 of the number of regions per quorum");
    eprintln!("k:           k - 1 no. of secondary joins in CCR before new primary joins are accepted");
    eprintln!("g:           desired number of nodes in a *region*");
    eprintln!("T:           number of iterations after initialization");
    eprintln!("seed:        random seed");
}

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() != 9 {
        usage(&args[0]);
        process::exit(1);
    }

    let valid_args: Vec<usize> = args.iter().enumerate()
        .filter(|(i, _)| *i != 0)
        .map(|(_, v)| v.parse::<usize>())
        .filter(|x| x.is_ok())
        .map(|x| x.unwrap())
        .collect();

    if valid_args.len() != 8 {
        usage(&args[0]);
        process::exit(1);
    }

    let h = valid_args[0];
    let m = valid_args[1];
    let lg_r = valid_args[2];
    let lg_c = valid_args[3];
    let k = valid_args[4];
    let g = valid_args[5];
    let iters = valid_args[6];
    let seed = valid_args[7];

    if (lg_c > lg_r) | (k > g) {
        usage(&args[0]);
        process::exit(1);
    }

    let blankregion = Region {
        num_honest: 0,
        num_malicious: 0,
        last_join: 0
    };

    let blankquorum = Quorum {
        tot_honest: 0,
        tot_malicious: 0,
        tot_last_join: 0,
        num_nodes_since_last_primary_join: 0,
    };

    let mut sim = Simulation {
        done_init: false,
        now: 0,
        rand: Pcg64::seed_from_u64(seed as u64),
        quorums: Vec::new(),
        regions: Vec::new(),
        lg_regions_per_quorum: lg_c,
        num_region_mask: (1<<lg_r)-1,
        cur_stats: CurrentStats::default(),
        cum_stats: CumulativeStats::default(),
        k,
        g,
    };

    sim.regions.resize(1<<lg_r, blankregion);
    sim.quorums.resize(1<<(lg_r-lg_c), blankquorum);

    eprintln!("Starting simulation h={} m={} 2^r={} regions 2^c={} regions/quorum k={} g={} desired quorum size T={} seed={}",
              h, m, 1 << lg_r, 1 << lg_c, k, g, iters, seed);

    sim.init(h, m);
    for iter in 0..iters {
        sim.move_malicious();
        if iter % 100000 == 0 {
            eprintln!("iter {}", iter);
        }
    }

    println!("h={} m={} 2^r={} 2^c={} k={} g={} T={} seed={}",
             h, m, 1 << lg_r, 1<< lg_c, k, g, iters, seed);
    sim.cum_stats.print();
}
