/// name = "rectree3"
/// version = "0.1.0"
/// authors = ["Simon Penel <simon.penel@univ-lyon1.fr>"]
/// release = "18/04/2021"
/// license = "CECILL-2.1"
/// Usage:
/// Read 2 recPhyloXML files and build svg representations
/// of 3 level reconciliation as gene/parasite/host."

use std::fs;
use std::env;
use std::process;
use getopt::Opt;
use webbrowser::{Browser};
use light_phylogeny::*;
use log::{info};
// use rectree3::*;

// Message d'erreur
// ----------------
fn display_help(programe_name:String) {
    const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
    const NAME: Option<&'static str> = option_env!("CARGO_PKG_NAME");
    const DESCRIPTION: Option<&'static str> = option_env!("CARGO_PKG_DESCRIPTION");
    println!("{} v{}", NAME.unwrap_or("unknown"),VERSION.unwrap_or("unknown"));
    println!("{}", DESCRIPTION.unwrap_or("unknown"));
    println!("Usage:");
    println!("{} -f parasite_host_input_file -g  gene_parasite_input_file/list_of_files [-b][-c config file][-h][-i][-I][-L][-m][-r ratio][-v]",programe_name);
    println!("    -b : open svg in browser");
    println!("    -c configfile: use a configuration file");
    println!("    -h : help");
    println!("    -i : display internal gene nodes");
    println!("    -I : display internal species nodes");
    println!("    -L : display as landscape");
    println!("    -m : use multiple parasite trees ");
    println!("    -r ratio : set the ratio between width of species and gene tree.");
    println!("               Default 1.0, you usualy do not need to change it. ");
    println!("    -v : verbose");
    println!("");
    println!("    Note on -b option : you must set a browser as default application for opening svg file");
    println!("");
    println!("Format of input files is recPhyloXML");
    println!("About recPhyloXML format: http://phylariane.univ-lyon1.fr/recphyloxml/");
    println!("recPhyloXML paper: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6198865/");
    process::exit(1);
}

fn main()  {
    // Initialise les options
    let mut options: Options =  Options::new();
    // Initialise la config
    let mut config: Config = Config::new();
    // Charge la config par deuakt si elle existe
    let fconf = "config_default.txt";
     if fs::metadata(fconf).is_ok() {
         set_config(fconf.to_string(), &mut config);

     }
    // Gestion des arguments et des options
    // ------------------------------------
    let args: Vec<String> = std::env::args().collect();
    if args.len() == 1 {
         display_help(args[0].to_string());
    }
    let mut opts = getopt::Parser::new(&args, "c:f:g:LbhiIr:v");
    let mut infile_gp = String::new();
    let mut infile_ph = String::new();
    let  outfile_gene_para = String::from("gene_para.svg");
    let  outfile_para_host = String::from("para_host.svg");
    let  outfile_mapped_1 = String::from("mapped_1.svg");
    let  outfile_mapped_2 = String::from("mapped_2.svg");
    let mut nb_args = 0;
    let mut _flag_mul = false;
    loop {
        match opts.next().transpose() {
            Err(err) => {
                eprintln!("Error : {}",err);
                std::process::exit(1);
            },
            Ok(res) => match res {
                None => break,
                Some(opt) => match opt {
                    Opt('i', None) => options.gene_internal = true,
                    Opt('I', None) => options.species_internal = true,
                    Opt('b', None) => options.open_browser = true,
                    Opt('r', Some(string)) => {
                        options.ratio = match string.parse::<f32>(){
                            Ok(valeur) => valeur,
                            Err(_err) => {
                                eprintln!("Error! Please give a numeric value with -r option");
                                process::exit(1);
                            },
                        };
                    },
                    Opt('L', None) => options.rotate = false,
                    Opt('m', None) =>  _flag_mul = true,
                    Opt('v', None) => {
                        options.verbose = true;
                        env::set_var("RUST_LOG", "info");
                        env_logger::init();
                        info!("Verbosity set to Info");
                        },
                    Opt('c', Some(string)) => {
                        set_config(string, &mut config);
                    },
                    Opt('f', Some(string)) => {
                        infile_ph = string.clone();
                        nb_args += 1;
                    },
                    Opt('g', Some(string)) => {
                        infile_gp = string.clone();
                        nb_args += 1;
                    },
                    Opt('h', None) => display_help(args[0].to_string()),
                    _ => unreachable!(),
                }
            }
        }
    }
    if nb_args != 2 {
         display_help(args[0].to_string());
    }
    let transfers = vec![]; // Initialise transfers
    // ---------------------------------------------------------
    // Create a structure Arena for the global parasite pipe
    // tree and a vector of structures Arena for gene path trees
    // ---------------------------------------------------------
    let mut global_pipe_parasite: ArenaTree<String> = ArenaTree::default();
    let mut global_roots: std::vec::Vec<usize> = Vec::new();
    let mut path_genes: std::vec::Vec<ArenaTree<String>> = Vec::new();
    // ---------------------------------------------------------
    // Fill global parasite pipe tree and is roots and path
    // genes trees
    // ---------------------------------------------------------
    read_recphyloxml_multi(infile_gp,&mut global_pipe_parasite,&mut path_genes,&mut global_roots);
    let  nb_gntree =  path_genes.len().clone();
    println!("Number of gene trees : {}",nb_gntree);
    info!("List of gene trees : {:?}",path_genes);
    let  nb_parasite_pipe =  global_roots.len().clone();
    println!("Number of parasite trees : {}",nb_parasite_pipe);
    println!("List of species trees roots : {:?}",global_roots);
    info!("Global parasite pipe tree : {:?}",global_pipe_parasite);
    // ---------------------------------------------------------
    // Generate svg of the lobal parasite pipe tree and  path
    // genes trees
    // ---------------------------------------------------------
    recphyloxml_processing(&mut global_pipe_parasite,&mut  path_genes, &mut options, &config,true,
            &transfers,outfile_gene_para);
    // ---------------------------------------------------------
    // Create a structure Arena for the host pipe tree and a
    // vector of structures Arena for parasite path trees
    // ---------------------------------------------------------
    let mut tree_host_pipe: ArenaTree<String> = ArenaTree::default();
    let mut global_roots: std::vec::Vec<usize> = Vec::new();
    let mut path_para_trees:std::vec::Vec<ArenaTree<String>> = Vec::new();
    // ---------------------------------------------------------
    // Fill  host pipe tree and is roots and path parasite trees
    // ---------------------------------------------------------
    read_recphyloxml_multi(infile_ph,&mut tree_host_pipe,&mut path_para_trees, &mut global_roots);
    let  nb_parasite_path =  path_para_trees.len().clone();
    println!("Number of pipe parasite trees in gene-parasite file : {}",nb_parasite_pipe);
    println!("Number of path parasite trees in parasite-host file : {}",nb_parasite_path);
    if nb_parasite_path != nb_parasite_pipe {
        println!();
        println!("==============================================");
        println!("Error! Different number of parasite trees in the 2 files!");
        println!("       Resulting svg will be incomplete.");
        println!("==============================================");
        println!();
    }
    // ---------------------------------------------------------
    // Generate svg of the host pipe tree and path parasite trees
    // ---------------------------------------------------------
    recphyloxml_processing(&mut tree_host_pipe,&mut  path_para_trees, &mut options, &config,
        true, &transfers,outfile_para_host);
    // ---------------------------------------------------------
    // Generation of first 3 levels svg
    // ---------------------------------------------------------
    info!("Parasite trees as a 'path tree' : {:?}",path_para_trees);
    info!("Parasite tree as a 'pipe tree' : {:?}",global_pipe_parasite);
    println!("==============================================");
    println!("Map parasite as 'path' to parasite as 'pipe'");
    println!("==============================================");
    let mut i = 0;
    while i < nb_parasite_pipe {
            map_parasite_g2s(&mut global_pipe_parasite, &mut path_para_trees[i]);
    i = i + 1;
    }
    info!("Global parasite tree wih events : {:?}",global_pipe_parasite);
    reset_pos(&mut global_pipe_parasite);
    let mut i = 0;
    while i < nb_gntree {
        reset_pos(&mut path_genes[i]);
        i = i + 1;
    }
    println!("==============================================");
    println!("Map parasite as 'species' to parasite as 'gene'");
    println!("==============================================");
    let mut i = 0;
    while i < nb_parasite_pipe {
        map_parasite_s2g(&mut global_pipe_parasite, &mut path_para_trees[i],&mut path_genes);
        i = i +  1;
    }
    info!("Global pipe parasite after mapping s2g : {:?}",global_pipe_parasite);
    println!("==============================================");
    println!("Map parasite as 'gene' to parasite as 'species' again");
    println!("==============================================");
    let mut i = 0;
    while i < nb_parasite_pipe {
        map_parasite_g2s(&mut global_pipe_parasite, &mut path_para_trees[i]);
        i = i + 1;
    }
    reset_pos(&mut global_pipe_parasite);
    let mut i = 0;
    while i < nb_gntree {
        reset_pos(&mut path_genes[i]);
        i = i + 1;
    }
    // attention on ne remape pas
    recphyloxml_processing(&mut global_pipe_parasite,&mut  path_genes, &mut options, &config,false,
            &transfers,outfile_mapped_1);
    // ---------------------------------------------------------
    // Generation of second 3 levels svg
    // ---------------------------------------------------------
    let mut i = 0;
    let gene_transfers = get_gtransfer(&mut path_genes[i]);
    info!("Transfers = {:?}",gene_transfers);
    let mut mapped_gene_transfers = map_transfer_mul(gene_transfers, &mut path_para_trees);
    info!("Mapped transfers = {:?}",mapped_gene_transfers);
    i = i + 1;
    while i < nb_gntree {
        let gene_transfers = get_gtransfer(&mut path_genes[i]);
        info!("Transfers = {:?}",gene_transfers);
        let mapped = map_transfer(gene_transfers, &mut path_para_trees[0]);
        info!("Mapped transfers = {:?}",mapped);
        for val in mapped {
            mapped_gene_transfers.push(val);
        }
        i = i + 1;
    }
    reset_pos(&mut tree_host_pipe);
    let mut i = 0;
    while i < nb_parasite_pipe {
        reset_pos(&mut path_para_trees[i]);
        i = i + 1;
    }
    println!("Building svg 2:  parasite tree within host pipe tree and mapped tarnsfers {}",
        outfile_mapped_2);
    // attention on ne remape pas
    recphyloxml_processing(&mut tree_host_pipe, &mut path_para_trees, &mut options, &config,
        false, &mapped_gene_transfers,outfile_mapped_2);
    let path = env::current_dir().expect("Unable to get current dir");
    let url_file = format!("file:///{}/{}", path.display(),"mapped_1.svg".to_string());
    if options.open_browser {
        if webbrowser::open_browser(Browser::Default,&url_file).is_ok() {
            info!("Browser OK");
        }
    }
    let url_file = format!("file:///{}/{}", path.display(),"mapped_2.svg".to_string());
    if options.open_browser {
        if webbrowser::open_browser(Browser::Default,&url_file).is_ok() {
            info!("Browser OK");
        }
    }
    reset_pos(&mut global_pipe_parasite);
    phyloxml_processing(&mut global_pipe_parasite, &mut options, &config,"para_simple.svg".to_string());
    reset_pos(&mut tree_host_pipe);
    phyloxml_processing(&mut tree_host_pipe, &mut options, &config,"host_simple.svg".to_string());
    let mut i = 0;
    while i < nb_parasite_pipe {
        reset_pos(&mut path_para_trees[i]);
        phyloxml_processing(&mut path_para_trees[i], &mut options, &config,("gene_simple_".to_owned()+&i.to_string()+".svg").to_string());
        i = i + 1;
    }
    println!("Output files:");

    println!(" - host_simple.svg ...... 1 level: host tree");
    let mut i = 0;
    while i < nb_parasite_pipe {
        println!(" - para_simple.svg ...... 2 levels: gene_simple_{}.svg",&i);
        i = i + 1;
    }
    println!(" - para_simple.svg ...... 2 levels: parasite tree(s)");
    println!(" - gene_para.svg ........ 2 levels: pipe parasite tree(s) with gene tree(s) inside");
    println!(" - para_host.svg ........ 2 levels: pipe host tree with parasite tree(s) inside");
    println!(" - mapped_1.svg ........  3 levels: reconciled pipe parasite tree(s) with gene tree(s)");
    println!(" - mapped_2.svg ........  3 levels: parasite-host reconciliation plus gene transfers");

    if nb_parasite_path != nb_parasite_pipe {
        println!();
        println!("==============================================");
        println!("Error! Different number of parasite trees in the 2 files!");
        println!("       Resulting svg will be incomplete.");
        println!("==============================================");
        println!();
    }
}

fn set_config(configfile: String, config: &mut Config) {
    let contents = fs::read_to_string(configfile);
    let contents = match contents {
        Ok(contents) => contents,
        Err(e) => {
            println!("Error: unable to read the config file");
            println!("{}",e);
            std::process::exit(1);

        },
    };
    let conf = contents.split('\n');
    for line in conf {
        let test: Vec<&str> = line.split(':').collect();
        if test.len() == 2 {
            match test[0] {
                "species_color" => {
                    info!("[set_config] species_color was {}",config.species_color);
                    config.species_color=test[1].to_string();
                    info!("[set_config] species_color is now {}",config.species_color);
                },
                "species_opacity" => {
                    info!("[set_config] species_opacity was {}",config.species_opacity);
                    config.species_opacity=test[1].to_string();
                    info!("[set_config] species_opacity is now {}",config.species_opacity);
                },
                "single_gene_color" => {
                    info!("[set_config] single_gene_color was {}",config.single_gene_color);
                    config.single_gene_color=test[1].to_string();
                    info!("[set_config] single_gene_color is now {}",config.single_gene_color);
                },
                "gene_opacity" => {
                    info!("[set_config] gene_opacity was {}",config.gene_opacity);
                    config.gene_opacity=test[1].to_string();
                    info!("[set_config] gene_opacity is now {}",config.gene_opacity);
                },
                "species_police_color" => {
                    info!("[set_config] species_police_color was {}",config.species_police_color);
                    config.species_police_color=test[1].to_string();
                    info!("[set_config] species_police_color is now {}",config.species_police_color);
                },
                "species_police_size" => {
                    info!("[set_config] species_police_size was {}",config.species_police_size);
                    config.species_police_size=test[1].to_string();
                    info!("[set_config] species_police_size is now {}",config.species_police_size);
                },
                "gene_police_size" => {
                    info!("[set_config] gene_police_size was {}",config.gene_police_size);
                    config.gene_police_size=test[1].to_string();
                    info!("[set_config] gene_police_size is now {}",config.gene_police_size);
                },
                "bezier" => {
                    info!("[set_config] bezier was {}",config.bezier);
                    config.bezier=test[1].to_string();
                    info!("[set_config] bezier is now {}",config.bezier);
                },
                _ => {},
            }
        }

    }
}
