/// name = "rectree3"
/// version = "0.1.0"
/// authors = ["Simon Penel <simon.penel@univ-lyon1.fr>"]
/// release = "18/04/2021"
/// license = "CECILL-2.1"
///
/// Usage:
/// Draw phylogenetic trees in a svg file.
/// Draw multiple reconciled gene trees with the associated species tree.
/// Draw simple gene or species tree too.
/// Read a newick, phyloxml or recPhyloXML file.
/// Format is guessed according to filename (default is newick).

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

// 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 [-b][-c config file][-h][-i][-I][-L][-o output file][-r ratio][-v]",programe_name);
    println!("    -b : open svg in browser");
    println!("    -c configfile: use a configuration file");
    // println!("    -F phylo/recphylo: force format phyloXML/recPhyloXML");
    // println!("    -g <n> : display the gene #n in phyloxml style (no species tree)");
    println!("    -h : help");
    println!("    -i : display internal gene nodes");
    println!("    -I : display internal species nodes");
    // println!("    -l factor : use branch length, using the given factor (default 1.0)");
    println!("    -L : display as landscape");
    // println!("    -o outputfile : set name of output file");
    // println!("    -p : build a phylogram");
    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!("    -s : drawing species tree only");
    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 = String::from("para.svg");
    let  outfile_para_rec = String::from("para_rec.svg");
    let  outfile_para_host = String::from("para_host.svg");
    let  outfile_host = String::from("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;
    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('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![];
    // On cree une structure Arena pour l'arbre d'espece
    // et un vecteur de  structures Arena pour le(s) arbres de gènes
    // -------------------------------------------------------------
    // Creation de la structure ArenaTree pour l'arbre d'espece
    // --------------------------------------------------------
    let mut tree_para_pipe: ArenaTree<String> = ArenaTree::default();
    // Creation du vecteur de structure ArenaTree pour les genes
    // ---------------------------------------------------------
    let mut gene_trees:std::vec::Vec<ArenaTree<String>> = Vec::new();
    read_recphyloxml(infile_gp,&mut tree_para_pipe,&mut gene_trees);
    let  nb_gntree =  gene_trees.len().clone();
    println!("Number of gene trees : {}",nb_gntree);
    info!("List of gene trees : {:?}",gene_trees);
    recphyloxml_processing(&mut tree_para_pipe,&mut  gene_trees, &mut options, &config,true,
        &transfers,outfile_gene_para);
    reset_pos(&mut tree_para_pipe);
    phyloxml_processing(&mut tree_para_pipe, &mut options, &config,outfile_para);

    // On cree une structure Arena pour l'arbre d'espece
    // et un vecteur de  structures Arena pour le(s) arbres de gènes
    // -------------------------------------------------------------
    // Creation de la structure ArenaTree pour l'arbre d'espece
    // --------------------------------------------------------
    let mut tree_host_pipe: ArenaTree<String> = ArenaTree::default();
    // Creation du vecteur de structure ArenaTree pour les genes
    // ---------------------------------------------------------
    let mut para_trees:std::vec::Vec<ArenaTree<String>> = Vec::new();
    read_recphyloxml(infile_ph,&mut tree_host_pipe,&mut para_trees);
    let  nb_paratree =  para_trees.len().clone();
    assert_eq!(nb_paratree,1,"I want only one parasite tree");
    println!("Number of gene trees : {}",nb_paratree);
    info!("List of gene trees : {:?}",para_trees);
    recphyloxml_processing(&mut tree_host_pipe,&mut  para_trees, &mut options, &config,
        true, &transfers,outfile_para_host);
    reset_pos(&mut tree_host_pipe);
    phyloxml_processing(&mut tree_host_pipe, &mut options, &config,outfile_host);
    reset_pos(&mut para_trees[0]);
    phyloxml_processing(&mut para_trees[0], &mut options, &config,outfile_para_rec);

    // Generation du svg 3 niveaux
    info!("Parasite trees as a 'gene tree' : {:?}",para_trees);
    info!("Parasite tree as a 'species tree' : {:?}",tree_para_pipe);
    println!("==============================================");
    println!("Map parasite as 'gene' to parasite as 'species'");
    println!("==============================================");
    map_parasite_g2s(&mut tree_para_pipe, &mut para_trees[0]);
    println!("==============================================");
    println!("Map parasite as 'species' to parasite as 'gene'");
    println!("==============================================");
    map_parasite_s2g(&mut tree_para_pipe, &mut para_trees[0], &mut gene_trees);
    println!("==============================================");
    println!("Map parasite as 'gene' to parasite as 'species' again");
    println!("==============================================");
    map_parasite_g2s(&mut tree_para_pipe, &mut para_trees[0]);
    if options.verbose {
        summary(&mut tree_para_pipe);
    }


    reset_pos(&mut tree_para_pipe);
    let mut i = 0;
    while i < nb_gntree {
    reset_pos(&mut gene_trees[i]);
    i = i + 1;
    }

    // attention on ne remape pas
    println!("Building svg 1:  gene trees within reconciled pipe parasite tree {}",outfile_mapped_1);
    recphyloxml_processing(&mut tree_para_pipe, &mut gene_trees, &mut options, &config, false,
        &transfers,outfile_mapped_1.to_string());
    let path = env::current_dir().expect("Unable to get current dir");
    let url_file = format!("file:///{}/{}", path.display(),outfile_mapped_1.to_string());
    if options.open_browser {
        if webbrowser::open_browser(Browser::Default,&url_file).is_ok() {
                info!("Browser OK");
        }
    }

    // Generation des svg hote parsite  +transfert gene

    let gene_transfers = get_gtransfer(&mut gene_trees[0]);
    info!("Transfers = {:?}",gene_transfers);
    let mut mapped_gene_transfers = map_transfer(gene_transfers, &mut para_trees[0]);
    info!("Mapped transfers = {:?}",mapped_gene_transfers);
    let mut i = 1;
    while i < nb_gntree {
        let gene_transfers = get_gtransfer(&mut gene_trees[i]);
        info!("Transfers = {:?}",gene_transfers);
        let mapped = map_transfer(gene_transfers, &mut 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);
    reset_pos(&mut para_trees[0]);
    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 para_trees, &mut options, &config,
        false, &mapped_gene_transfers,outfile_mapped_2.to_string());
    let path = env::current_dir().expect("Unable to get current dir");
    let url_file = format!("file:///{}/{}", path.display(),outfile_mapped_2.to_string());

    if options.open_browser {
            if webbrowser::open_browser(Browser::Default,&url_file).is_ok() {
                    info!("Browser OK");
            }
        }
    println!("Other files:");
    println!(" - gene_para.svg ........ gene-parasite reconciliation");
    println!(" - para.svg ............. parasite tree");
    println!(" - para_rec.svg ......... reconciled parasite tree");
    println!(" - para_host.svg ........ parasite-host reconciliation");
    println!(" - host.svg ............. host tree");
}

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);
                },
                _ => {},
            }
        }

    }
}
