import subprocess
import os, sys, argparse, logging, re
from argparse import ArgumentParser
import pandas as pd


logger = logging.getLogger('main')
logger.setLevel(level=logging.INFO)
logging.basicConfig(level=logging.INFO,
                    datefmt='%Y/%m/%d %H:%M:%S',
                    format='[%(asctime)s %(levelname)s] %(message)s')

BLASTP_THREAD = 16
KS_PROCESS = 16
KS_ALIGN = "muscle"
SOFTWARE = "quota_Anchor"
RAW_PEP_SUFFIX = ".raw.pep"
LONGEST_PEP_SUFFIX = ".longest.pep"
RAW_CDS_SUFFIX = ".raw.cds"
LONGEST_CDS_SUFFIX = ".longest.cds"
GFF3_SUFFIX = ".gff3"
FASTA_SUFFIX = ".fa"
LENGTH_SUFFIX = ".length.txt"
FIGURE_SUFFIX = ".png"

def search_input_dir_files(input_directory):
    _genome_lst = []
    _gff_lst = []
    _species_lst = []

    abs_input_dir = os.path.abspath(input_directory)

    files_and_folders = os.listdir(abs_input_dir)
    for name in files_and_folders:
        abs_path = os.path.join(abs_input_dir, name)
        if os.path.isfile(abs_path) and name.endswith(FASTA_SUFFIX):
            _genome_lst.append(abs_path)
            _species_lst.append(name[:-3])
        elif os.path.isfile(abs_path) and name.endswith(GFF3_SUFFIX):
            _gff_lst.append(abs_path)
        else:
            pass
    _genome_lst.sort()
    _gff_lst.sort()
    _species_lst.sort()
    return _genome_lst, _gff_lst, _species_lst

def parse_species_pair_file(file):
    """
    file: This species pair file is generated by the quota_Anchor trios procedure
    e.g.
    Species_1	Species_2	r_value	q_value	get_all_collinear_pairs
    maize	sorghum	1	1	0
    maize	setaria	1	1	0
    sorghum	setaria	1	1	0
    maize	oryza	1	1	0
    sorghum	oryza	1	1	0
    setaria	oryza	1	1	0

    return: List[Tuple[str, str]], pd.DataFrame
    """
    df = pd.read_csv(file, header=0, index_col=None)
    df["Species_1"] = df["Species_1"].astype(str)
    df["Species_2"] = df["Species_2"].astype(str)
    pair_series = df.apply(lambda x: (x["Species_1"], x["Species_2"]), axis=1)
    pair_lst = pair_series.to_list()

    r_value_map = {}
    q_value_map = {}
    get_all_collinearity = {}
    for _, row in df.iterrows():
        r_value_map[str(row["Species_1"]) + "\t" + str(row["Species_2"])] = str(row["r_value"])
        q_value_map[str(row["Species_1"]) + "\t" + str(row["Species_2"])] = str(row["q_value"])
        get_all_collinearity[str(row["Species_1"]) + "\t" + str(row["Species_2"])] = str(row["get_all_collinear_pairs"])
    return pair_lst, r_value_map, q_value_map, get_all_collinearity

def output_info(info):
    if info:
        info_list = info.split("\n")
        for i in info_list:
            if i:
               print(f'{i}')
    print()
    return


class SpeciesPair:
    def __init__(self, query_species, ref_species, r_value_map, q_value_map, get_all_collinearity_map, overwrite):
        self.query_species = query_species
        self.ref_species = ref_species
        self.r_value_map = r_value_map
        self.q_value_map = q_value_map
        self.get_all_collinearity_map = get_all_collinearity_map
        self.overwrite = overwrite

        self.query_gff3_path = os.path.join(INPUT_DIR, self.query_species + GFF3_SUFFIX)
        self.ref_gff3_path = os.path.join(INPUT_DIR, self.ref_species + GFF3_SUFFIX)

        self.query_protein_path =  os.path.join(OUTPUT_DIR, "01longest", self.query_species + LONGEST_PEP_SUFFIX)
        self.ref_protein_path = os.path.join(OUTPUT_DIR, "01longest", self.ref_species + LONGEST_PEP_SUFFIX)
        self.query_cds_path = os.path.join(OUTPUT_DIR, "01longest", self.query_species + LONGEST_CDS_SUFFIX)
        self.ref_cds_path = os.path.join(OUTPUT_DIR, "01longest", self.ref_species + LONGEST_CDS_SUFFIX)
        self.ref_db_path = os.path.join(OUTPUT_DIR, "02synteny", "tmp", self.ref_species + "_" + f'{ALIGN}' + ".db")
        self.blast_path = os.path.join(OUTPUT_DIR, "02synteny", "tmp", self.query_species + "_" + self.ref_species + ".blast")
        self.table_path = os.path.join(OUTPUT_DIR, "02synteny", self.query_species + "_" + self.ref_species + ".table")
        self.ref_length = os.path.join(LENGTH_DIR, self.ref_species + LENGTH_SUFFIX)
        self.query_length = os.path.join(LENGTH_DIR, self.query_species + LENGTH_SUFFIX)
        name = self.query_species + "_" + self.ref_species + self.get_all_collinearity_map[self.query_species + "\t" + self.ref_species]
        self.collinearity_path =  os.path.join(OUTPUT_DIR, "02synteny", name + ".collinearity")
        self.ks_path = os.path.join(OUTPUT_DIR, "02synteny", name + ".ks")
        self.pep_path = str(self.query_protein_path) + "," + str(self.ref_protein_path)
        self.cds_path = str(self.query_cds_path) + "," + str(self.ref_cds_path)

    def quota_anchor_pre_col(self):
        if self.overwrite:
            command_line = [SOFTWARE,
                            'pre_col',
                            '-a', ALIGN,
                            '-rs', self.ref_protein_path,
                            '-qs', self.query_protein_path,
                            '-db', self.ref_db_path,
                            '-mts', "20",
                            '-e', "1e-10",
                            '-b', self.blast_path,
                            '-rg', self.ref_gff3_path,
                            '-qg', self.query_gff3_path,
                            '-o', self.table_path,
                            '-bs', "100",
                            '-al', "0",
                            '--outfmt', "6",
                            '--dtype', "prot",
                            '-t',  str(BLASTP_THREAD),
                            '-rl', self.ref_length,
                            '-ql', self.query_length,
                            '--overwrite'
                            ]
        else:
            command_line = [SOFTWARE,
                            'pre_col',
                            '-a', ALIGN,
                            '-rs', self.ref_protein_path,
                            '-qs', self.query_protein_path,
                            '-db', self.ref_db_path,
                            '-mts', "20",
                            '-e', "1e-10",
                            '-b', self.blast_path,
                            '-rg', self.ref_gff3_path,
                            '-qg', self.query_gff3_path,
                            '-o', self.table_path,
                            '-bs', "100",
                            '-al', "0",
                            '--outfmt', "6",
                            '--dtype', "prot",
                            '-t',  str(BLASTP_THREAD),
                            '-rl', self.ref_length,
                            '-ql', self.query_length,
                            ]
        try:
            result = subprocess.run(command_line, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
            stderr_pre_col = result.stderr
            stdout_pre_col = result.stdout
            output_info(stdout_pre_col)
            output_info(stderr_pre_col)

        except subprocess.CalledProcessError as e:
            pattern = re.compile(r'(set)\s+\'(--overwrite)\'\s+(in)\s+(the)\s+(command)\s+(line)\s+(to)\s+(overwrite)\s+(it)\.')
            error_message = e.stderr
            output_info(error_message)

            output_message = e.stdout
            output_info(output_message)

            if not re.search(pattern, error_message) and not re.search(pattern, output_message):
                sys.exit(1)
            pass

    def quota_anchor_col(self):
        if self.overwrite:
            command_line = [SOFTWARE,
                            'col',
                            '-i', self.table_path,
                            '-o', self.collinearity_path,
                            '-r', self.r_value_map[self.query_species + "\t" + self.ref_species],
                            '-q', self.q_value_map[self.query_species + "\t" + self.ref_species],
                            '--strict_strand', "1",
                            '--get_all_collinearity', self.get_all_collinearity_map[self.query_species + "\t" + self.ref_species],
                            '--count_style', "0",
                            '--tandem_length', "500",
                            '--overlap_window', "1",
                            '--maximum_gap_size', "25",
                            '--minimum_chain_score', "4",
                            '--gap_extend_penalty', "-0.005",
                            '--strict_remove_overlap', "0",
                            '--overwrite'
                            ]
        else:
            command_line = [SOFTWARE,
                            'col',
                            '-i', self.table_path,
                            '-o', self.collinearity_path,
                            '-r', self.r_value_map[self.query_species + "\t" + self.ref_species],
                            '-q', self.q_value_map[self.query_species + "\t" + self.ref_species],
                            '--strict_strand', "1",
                            '--get_all_collinearity', self.get_all_collinearity_map[self.query_species + "\t" + self.ref_species],
                            '--count_style', "0",
                            '--tandem_length', "500",
                            '--overlap_window', "1",
                            '--maximum_gap_size', "25",
                            '--minimum_chain_score', "4",
                            '--gap_extend_penalty', "-0.005",
                            '--strict_remove_overlap', "0",
                            ]
        try:
            result = subprocess.run(command_line, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
            stderr_col = result.stderr
            stdout_col = result.stdout
            output_info(stdout_col)
            output_info(stderr_col)

        except subprocess.CalledProcessError as e:
            pattern = re.compile(
                r'(set)\s+\'(--overwrite)\'\s+(in)\s+(the)\s+(command)\s+(line)\s+(to)\s+(overwrite)\s+(it)\.')
            error_message = e.stderr
            output_info(error_message)

            output_message = e.stdout
            output_info(output_message)

            if not re.search(pattern, error_message) and not re.search(pattern, output_message):
                sys.exit(1)
            pass

    def quota_anchor_ks(self):
        if self.overwrite:
            command_line = [SOFTWARE,
                            'ks',
                            '-i', self.collinearity_path,
                            '-a', KS_ALIGN,
                            '-p', self.pep_path,
                            '-d', self.cds_path,
                            '-o', self.ks_path,
                            '-t', str(KS_PROCESS),
                            '--overwrite'
                            ]
        else:
            command_line = [SOFTWARE,
                            'ks',
                            '-i', self.collinearity_path,
                            '-a', KS_ALIGN,
                            '-p', self.pep_path,
                            '-d', self.cds_path,
                            '-o', self.ks_path,
                            '-t', str(KS_PROCESS),
                            '--add_ks'
                            ]
        try:
            result = subprocess.run(command_line, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
            stderr_ks = result.stderr
            stdout_ks = result.stdout
            output_info(stdout_ks)
            output_info(stderr_ks)

        except subprocess.CalledProcessError as e:
            pattern = re.compile(
                r'(set)\s+\'(--overwrite)\'\s+(in)\s+(the)\s+(command)\s+(line)\s+(to)\s+(overwrite)\s+(it)\.')
            error_message = e.stderr
            output_info(error_message)

            output_message = e.stdout
            output_info(output_message)

            if not re.search(pattern, error_message) and not re.search(pattern, output_message):
                sys.exit(1)
            pass

    def run(self):
        os.makedirs(os.path.join(OUTPUT_DIR, "02synteny", "tmp"), exist_ok=True)
        if not SKIP_TABLE:
            self.quota_anchor_pre_col()
        if not SKIP_COLLINEARITY:
            self.quota_anchor_col()
        if not SKIP_KS:
            self.quota_anchor_ks()
        return


class SpeciesPairPlot:
    def __init__(self, query_species, ref_species, get_all_collinearity_map, overwrite):
        self.query_species = query_species
        self.ref_species = ref_species
        self.get_all_collinearity_map = get_all_collinearity_map
        self.overwrite = overwrite

        self.output_plot_path = os.path.join(OUTPUT_DIR, "03plot", self.query_species + "_" + self.ref_species)
        self.table_path = os.path.join(OUTPUT_DIR, "02synteny", self.query_species + "_" + self.ref_species + ".table")
        self.ref_length = os.path.join(LENGTH_DIR, self.ref_species + LENGTH_SUFFIX)
        self.query_length = os.path.join(LENGTH_DIR, self.query_species + LENGTH_SUFFIX)

        self.collinearity_path =  os.path.join(OUTPUT_DIR, "02synteny", self.query_species + "_" + self.ref_species +
                                               get_all_collinearity_map[self.query_species + "\t" + self.ref_species] + ".collinearity")

        self.ks_path = os.path.join(OUTPUT_DIR, "02synteny", self.query_species + "_" + self.ref_species
                                    + self.get_all_collinearity_map[self.query_species + "\t" + self.ref_species] + ".ks")

    def quota_anchor_dotplot_identity(self, input_file):
        if self.overwrite:
            command_line = [SOFTWARE,
                            'dotplot',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + f'{FIGURE_SUFFIX}'),
                            '-r', self.ref_length,
                            '-q', self.query_length,
                            '-r_label', self.ref_species,
                            '-q_label', self.query_species,
                            '-use_identity',
                            '--overwrite'
                            ]
        else:
            command_line = [SOFTWARE,
                            'dotplot',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + f'{FIGURE_SUFFIX}'),
                            '-r', self.ref_length,
                            '-q', self.query_length,
                            '-r_label', self.ref_species,
                            '-q_label', self.query_species,
                            '-use_identity',
                            ]
        try:
            result = subprocess.run(command_line, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
            stderr_identity = result.stderr
            stdout_identity = result.stdout
            output_info(stdout_identity)
            output_info(stderr_identity)

        except subprocess.CalledProcessError as e:
            pattern = re.compile(
                r'(set)\s+\'(--overwrite)\'\s+(in)\s+(the)\s+(command)\s+(line)\s+(to)\s+(overwrite)\s+(it)\.')
            error_message = e.stderr
            output_info(error_message)

            output_message = e.stdout
            output_info(output_message)

            if not re.search(pattern, error_message) and not re.search(pattern, output_message):
                sys.exit(1)
            pass

    def quota_anchor_dotplot_ks(self, input_file, ks_file):
        if self.overwrite:
            command_line = [SOFTWARE,
                            'dotplot',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + ".ks" + f'{FIGURE_SUFFIX}'),
                            '-r', self.ref_length,
                            '-q', self.query_length,
                            '-r_label', self.ref_species,
                            '-q_label', self.query_species,
                            '-ks', ks_file ,
                            '--overwrite'
                            ]
        else:
            command_line = [SOFTWARE,
                            'dotplot',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + ".ks" + f'{FIGURE_SUFFIX}'),
                            '-r', self.ref_length,
                            '-q', self.query_length,
                            '-r_label', self.ref_species,
                            '-q_label', self.query_species,
                            '-ks', ks_file ,
                            ]
        try:
            result = subprocess.run(command_line, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
            stderr_ks = result.stderr
            stdout_ks = result.stdout
            output_info(stdout_ks)
            output_info(stderr_ks)

        except subprocess.CalledProcessError as e:
            pattern = re.compile(
                r'(set)\s+\'(--overwrite)\'\s+(in)\s+(the)\s+(command)\s+(line)\s+(to)\s+(overwrite)\s+(it)\.')
            error_message = e.stderr
            output_info(error_message)

            output_message = e.stdout
            output_info(output_message)

            if not re.search(pattern, error_message) and not re.search(pattern, output_message):
                sys.exit(1)
            pass

    def quota_anchor_circle(self, input_file):
        if self.overwrite:
            command_line = [SOFTWARE,
                            'circle',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + ".circle" + f'{FIGURE_SUFFIX}'),
                            '-r', self.ref_length,
                            '-q', self.query_length,
                            '-rn', self.ref_species,
                            '-qn', self.query_species,
                            '-rm', "chr,Chr,CHR",
                            '-cf', "12",
                            '-sf', "12",
                            '-fs',  "14,14",
                            '--overwrite'
                            ]
        else:
            command_line = [SOFTWARE,
                            'circle',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + ".circle" + f'{FIGURE_SUFFIX}'),
                            '-r', self.ref_length,
                            '-q', self.query_length,
                            '-rn', self.ref_species,
                            '-qn', self.query_species,
                            '-rm', "chr,Chr,CHR",
                            '-cf', "12",
                            '-sf', "12",
                            '-fs',  "14,14",
                            ]
        try:
            result = subprocess.run(command_line, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
            stderr_circle = result.stderr
            stdout_circle = result.stdout
            output_info(stdout_circle)
            output_info(stderr_circle)

        except subprocess.CalledProcessError as e:
            pattern = re.compile(
                r'(set)\s+\'(--overwrite)\'\s+(in)\s+(the)\s+(command)\s+(line)\s+(to)\s+(overwrite)\s+(it)\.')
            error_message = e.stderr
            output_info(error_message)

            output_message = e.stdout
            output_info(output_message)

            if not re.search(pattern, error_message) and not re.search(pattern, output_message):
                sys.exit(1)
            pass

    def quota_anchor_line(self, input_file):
        if self.overwrite:
            command_line = [SOFTWARE,
                            'line',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + ".line" + f'{FIGURE_SUFFIX}'),
                            '-l', f'{self.ref_length}' +"," + f'{self.query_length}',
                            '-n', f'{self.ref_species}' + "," +f'{self.query_species}',
                            '-rm', "chr,Chr,CHR",
                            '-cf', "7",
                            '-sf', "12",
                            '-fs',  "14,14",
                            '--overwrite'
                            ]
        else:
            command_line = [SOFTWARE,
                            'line',
                            '-i', input_file,
                            '-o', os.path.join(str(self.output_plot_path), os.path.basename(input_file) + ".line" + f'{FIGURE_SUFFIX}'),
                            '-l', f'{self.ref_length}' +"," + f'{self.query_length}',
                            '-n', f'{self.ref_species}' + "," +f'{self.query_species}',
                            '-rm', "chr,Chr,CHR",
                            '-cf', "7",
                            '-sf', "12",
                            '-fs',  "14,14"
                            ]
        try:
            result = subprocess.run(command_line, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
            stderr_line = result.stderr
            stdout_line = result.stdout
            output_info(stdout_line)
            output_info(stderr_line)

        except subprocess.CalledProcessError as e:
            pattern = re.compile(
                r'(set)\s+\'(--overwrite)\'\s+(in)\s+(the)\s+(command)\s+(line)\s+(to)\s+(overwrite)\s+(it)\.')
            error_message = e.stderr
            output_info(error_message)

            output_message = e.stdout
            output_info(output_message)

            if not re.search(pattern, error_message) and not re.search(pattern, output_message):
                sys.exit(1)
            pass

    def run(self):
        os.makedirs(os.path.join(OUTPUT_DIR, "03plot", self.query_species + "_" + self.ref_species), exist_ok=True)
        if PLOT_TABLE:
            self.quota_anchor_dotplot_identity(self.table_path)
        if PLOT_COLLINEARITY:
            self.quota_anchor_dotplot_identity(self.collinearity_path)
            if os.path.exists(self.ks_path) and os.path.getsize(self.ks_path) > 0:
                self.quota_anchor_dotplot_ks(self.collinearity_path, self.ks_path)
            self.quota_anchor_circle(self.collinearity_path)
            self.quota_anchor_line(self.collinearity_path)
        return


if __name__ == '__main__':
    parser = ArgumentParser(usage="Generate syntenic gene pairs synonymous substitution rate for each species pair.", formatter_class=argparse.RawDescriptionHelpFormatter, description= """
    The initial goal of this script is to simplify the user pipeline of quota_Anchor positioning wgd events relative to species divergent events.
    Note:
    1. The directory corresponding to the input_dir parameter needs to contain genome files and genome annotation files with .gff3 suffix and .fa suffix.
    
    2. After the longest_pep or longest_cds step, fai files will be generated in the folder where the genome file and annotation file are located.
       And protein and cds files will be generated in output_dir/01longest folder.
       The following is the current directory tree.
                                    ├── output_dir
                                    │     └── 01longest
                                    │         ├── maize.longest.cds
                                    │         ├── maize.longest.pep
                                    │         ├── maize.raw.cds
                                    │         ├── maize.raw.pep
                                    │         ├── merged.longest.cds
                                    │         ├── merged.longest.pep
                                    │         ├── oryza.longest.cds
                                    │         ├── oryza.longest.pep
                                    │         ├── oryza.raw.cds
                                    │         ├── oryza.raw.pep
                                    │         ├── setaria.longest.cds
                                    │         ├── setaria.longest.pep
                                    │         ├── setaria.raw.cds
                                    │         ├── setaria.raw.pep
                                    │         ├── sorghum.longest.cds
                                    │         ├── sorghum.longest.pep
                                    │         ├── sorghum.raw.cds
                                    │         └── sorghum.raw.pep
                                    ├── raw_data
                                    │     ├── maize.fa
                                    │     ├── maize.fa.fai
                                    │     ├── maize.gff3
                                    │     ├── oryza.fa
                                    │     ├── oryza.fa.fai
                                    │     ├── oryza.gff3
                                    │     ├── setaria.fa
                                    │     ├── setaria.fa.fai
                                    │     ├── setaria.gff3
                                    │     ├── sorghum.fa
                                    │     ├── sorghum.fa.fai
                                    │     └── sorghum.gff3
                                    └── scripts
                                        ├── ks_pipeline.py
                                        └── longest_pipeline.py

    3. Execute the following command in your system terminal to get the species chromosome length file. 
       You may need to understand the meaning of the -s parameter via quota_Anchor get_chr_length command
       
       quota_Anchor get_chr_length -f "$(find ./raw_data/*fai |awk '{printf "%s,", $1}')" -g "$(find ./raw_data/*gff3 |awk '{printf "%s,", $1}')" -s 0-9,CHR,chr,Chr:0-9,CHR,chr,Chr:0-9,CHR,chr,Chr:0-9,CHR,chr,Chr -o "$(find ./raw_data/*gff3 |awk '{printf "%s,", $1}'|sed s/gff3/length\\.txt/g)" --overwrite
    
    4. Execute the following command in your system terminal to get trios file and species pair file according to binary newick tree.
       
       quota_Anchor trios -n "(((maize, sorghum), setaria), oryza);" -k "maize" -ot ortholog_trios_maize.csv -op species_pairs.csv -t tree.txt --overwrite
    
    5. The following is the current directory tree.
                                    ├── ortholog_trios_maize.csv
                                    ├── output_dir
                                    │     └── 01longest
                                    │         ├── maize.longest.cds
                                    │         ├── maize.longest.pep
                                    │         ├── maize.raw.cds
                                    │         ├── maize.raw.pep
                                    │         ├── merged.longest.cds
                                    │         ├── merged.longest.pep
                                    │         ├── oryza.longest.cds
                                    │         ├── oryza.longest.pep
                                    │         ├── oryza.raw.cds
                                    │         ├── oryza.raw.pep
                                    │         ├── setaria.longest.cds
                                    │         ├── setaria.longest.pep
                                    │         ├── setaria.raw.cds
                                    │         ├── setaria.raw.pep
                                    │         ├── sorghum.longest.cds
                                    │         ├── sorghum.longest.pep
                                    │         ├── sorghum.raw.cds
                                    │         └── sorghum.raw.pep
                                    ├── raw_data
                                    │     ├── maize.fa
                                    │     ├── maize.fa.fai
                                    │     ├── maize.gff3
                                    │     ├── maize.length.txt
                                    │     ├── oryza.fa
                                    │     ├── oryza.fa.fai
                                    │     ├── oryza.gff3
                                    │     ├── oryza.length.txt
                                    │     ├── setaria.fa
                                    │     ├── setaria.fa.fai
                                    │     ├── setaria.gff3
                                    │     ├── setaria.length.txt
                                    │     ├── sorghum.fa
                                    │     ├── sorghum.fa.fai
                                    │     ├── sorghum.gff3
                                    │     └── sorghum.length.txt
                                    ├── scripts
                                    │     ├── ks_pipeline.py
                                    │     └── longest_pipeline.py
                                    ├── species_pairs.csv
                                    └── tree.txt

    Example:
    1. python ./scripts/ks_pipeline.py -i raw_data -o output_dir -s species_pairs.csv -a diamond -l raw_data --overwrite -plot_table
    
    Other usage:
    a)When you simply want to get all syntenic pairs (disable r_value and q_value in collinearity procedure).
      you can manually modify the get_all_collinear_pairs column of species_pair_file to 1.
    b)The script adjusts the parameters of the collinearity procedure based on the r_value q_value and get_all_collinear_pairs columns of the species pairs file.
      You may need to understand the meaning of the r_value, q_value and get_all_collinear_pairs parameter via quota_Anchor col command.
    
    Species_1	Species_2	r_value	q_value	get_all_collinear_pairs
    maize	sorghum	1	1	0                                                       
    maize	setaria	1	1	0
    sorghum	setaria	1	1	0
    maize	oryza	1	1	0
    sorghum	oryza	1	1	0
    setaria	oryza	1	1	0
    """)
    parser.add_argument("-i", "--input_dir",
                        dest="input_dir",
                        type=str,
                        default="",
                        metavar="",
                        help="Path to the directory containing genome files and genome annotation files with .gff3 and .fa suffix")
    parser.add_argument("-o", "--output_dir",
                        dest="output_dir",
                        type=str,
                        default="",
                        metavar="",
                        help="If the path does not exist, the script will recursively create folders in this path.")
    parser.add_argument("-s", "--species_pair_file",
                        dest="species_pair_file",
                        type=str,
                        default="",
                        metavar="",
                        help="This species pair file is generated by the quota_Anchor trios command")
    parser.add_argument("-a", "--align",
                        dest="align",
                        choices=["diamond", "blastp"],
                        metavar="",
                        help="Fill diamond or blastp for pairwise protein alignment")
    parser.add_argument("-l", "--length_path",
                        dest="length_path",
                        type=str,
                        default="",
                        metavar="",
                        help="Path to the directory containing chromosome length file with .length.txt suffix")
    parser.add_argument("-skip_table", "--skip_table",
                        dest="skip_table",
                        action='store_true',
                        help="Skip generating table file for each specie pair")
    parser.add_argument("-skip_collinearity", "--skip_collinearity",
                        dest="skip_collinearity",
                        action='store_true',
                        help="Skip generating collinearity file for each species pair.")
    parser.add_argument("-skip_ks", "--skip_ks",
                        dest="skip_ks",
                        action='store_true',
                        help="Skip generating ks file for each species pair.")
    parser.add_argument("-plot_table", "--plot_table",
                        dest="plot_table",
                        action='store_true',
                        help="Plot table file(modified blast file) for each species pair")
    parser.add_argument("-plot_collinearity", "--plot_collinearity",
                        dest="plot_collinearity",
                        action='store_true',
                        help="Plot collinearity file for each species pair")
    parser.add_argument("-overwrite", "--overwrite",
                        dest="overwrite",
                        action='store_true',
                        help="Overwrite the output file.")
    args = parser.parse_args()
    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(0)
    if args.input_dir == "":
        logger.error("Error: please specify --input_dir.")
        parser.print_help()
        sys.exit(1)
    if args.output_dir == "":
        logger.error("Error: please specify --output_dir.")
        parser.print_help()
        sys.exit(1)
    if args.species_pair_file == "":
        logger.error("Error: please specify --species_pair_file.")
        parser.print_help()
        sys.exit(1)
    INPUT_DIR = args.input_dir
    LENGTH_DIR = args.length_path
    OUTPUT_DIR = args.output_dir
    SPECIES_PAIR_FILE = args.species_pair_file
    ALIGN = args.align
    SKIP_TABLE = args.skip_table
    SKIP_COLLINEARITY = args.skip_collinearity
    SKIP_KS = args.skip_ks
    PLOT_TABLE = args.plot_table
    PLOT_COLLINEARITY = args.plot_collinearity
    OVERWRITE = args.overwrite

    # First, get_species_lst
    _, _, SPECIES_LST = search_input_dir_files(INPUT_DIR)

    # Second, get each species pair.
    pair_list, R_VALUE_MAP, Q_VALUE_MAP, GET_ALL_COLLINEARITY_MAP = parse_species_pair_file(SPECIES_PAIR_FILE)

    # Third, Loop each species pair
    for species_1, species_2 in pair_list:
        SpeciesPair(species_1, species_2, R_VALUE_MAP, Q_VALUE_MAP, GET_ALL_COLLINEARITY_MAP, OVERWRITE).run()
        if PLOT_TABLE or PLOT_COLLINEARITY:
            SpeciesPairPlot(species_1, species_2, GET_ALL_COLLINEARITY_MAP, OVERWRITE).run()



