#!/usr/bin/env python

import argparse
import os
import sys
import glob


def __print_msg(msg, exit=False, compared_fpaths=None):
    if compared_fpaths is not None:
        msg = 'Comparing files %s AND %s: ' % (os.path.basename(compared_fpaths[0]),
                                               os.path.basename(compared_fpaths[1])) \
              + msg
    print(msg)
    if exit:
        sys.exit(1)


def __gene_lists_diff(gene_list1, gene_list2, fpath1, fpath2, stop_on_error=False):
    def __gene_entry_to_core_info(entry):
        entry = entry.strip()
        gene_id, start, end, strand, smCOG, locus_tag, annotation = entry.split('\t')
        return start, end, strand, smCOG, annotation

    def __pretty_print_gene_set(gene_set):
        return '\n'.join(map(lambda x: '\t'.join(list(x)), sorted(gene_set, key=lambda x: int(x[0]))))

    gene_set1 = set([__gene_entry_to_core_info(entry) for entry in gene_list1])
    gene_set2 = set([__gene_entry_to_core_info(entry) for entry in gene_list2])
    if gene_set1 == gene_set2:
        return 0
    __print_msg("Error! Content of input files do not match!\n"
                "\tExtra entries in converted file (%s):\n%s\n"
                "\t\tvs\n"
                "\tExtra entries in original file (%s):\n%s" %
                (fpath1, __pretty_print_gene_set(gene_set1 - gene_set2),
                 fpath2, __pretty_print_gene_set(gene_set2 - gene_set1)), exit=stop_on_error)
    return 1

def main():
    parser = argparse.ArgumentParser(description='Tester of the antiSMASH v5 converter: compares its output '
                                                 '(txt/*_gene.txt and *_NRPS_PKS.txt files) to original antiSMASH v3 files')
    parser.add_argument(
        '-1', '--converter-out',
        metavar='FILE/DIR',
        type=str,
        default=None,
        required=True,
        help='*_gene.txt, *_NRPS_PKS.txt file or the entire "txt" dir generated by the converter'
    )
    parser.add_argument(
        '-2', '--original-out',
        metavar='FILE/DIR',
        type=str,
        default=None,
        required=True,
        help='*_gene.txt, *_NRPS_PKS.txt file or the entire "txt" dir generated by antiSMASH v3'
    )
    parser.add_argument(
        '--stop-on-error',
        action='store_true',
        help='Stop processing on the first error.'
    )
    parser.add_argument(
        '--stop-on-mismatch',
        action='store_true',
        help='Stop processing on the first mismatch.'
    )
    # parser.add_argument(
    #     '--allow-skipping',
    #     action='store_true',
    #     help='Try to skip original line if a crucial mismatch is detected.'
    # )
    parser.add_argument(
        '--type',
        choices=['gene', 'NRPS_PKS'],
        default='gene',
        help='Checking mode ("gene" or "NRPS_PKS" files), default: "%(default)s"'
    )

    args = parser.parse_args()

    if not os.path.exists(args.converter_out) or not os.path.exists(args.original_out):
        print("Error! At least one of the input paths does not exist!")
        sys.exit(1)

    converter_fpaths = []
    original_fpaths = []
    for args_out, fpaths in [(args.converter_out, converter_fpaths), (args.original_out, original_fpaths)]:
        if os.path.isfile(args_out):
            fpaths.append(args_out)
        elif os.path.isdir(args_out):
            basedir = args_out
            if os.path.isdir(os.path.join(basedir, 'txt')):
                basedir = os.path.join(basedir, 'txt')
            fpaths += glob.glob(os.path.join(basedir, '*_%s.txt' % args.type))
        else:
            print("Error! args_out is neither a file nor a dir!")
            sys.exit(1)

    if len(converter_fpaths) != len(original_fpaths) and (len(converter_fpaths) == 0 or len(original_fpaths) == 0):
        print("Error! Number of input paths (*_gene/NRPS_PKS.txt) specified via either -1 or -2 is zero!")
        sys.exit(1)

    converter_fnames = set(map(os.path.basename, converter_fpaths))
    original_fnames = set(map(os.path.basename, original_fpaths))
    if converter_fnames != original_fnames:
        converter_basedir = os.path.dirname(converter_fpaths[0])
        original_basedir = os.path.dirname(original_fpaths[0])
        extra_converter_fnames_str = " ".join(sorted(list(converter_fnames - original_fnames)))
        extra_original_fnames_str = " ".join(sorted(list(original_fnames - converter_fnames)))

        __print_msg("Error! Basenames of input paths (*_gene/NRPS_PKS.txt) does not match in -1 and -2!\n"
                    "\tExtra filenames in converted (basedir: %s): %s\n"
                    "\t\tvs\n"
                    "\tExtra filenames in original (basedir: %s): %s" %
                    (converter_basedir, extra_converter_fnames_str,
                     original_basedir, extra_original_fnames_str), exit=args.stop_on_error)
        common_fnames = converter_fnames & original_fnames
        __print_msg("Note: will proceed with the common set of filenames (len = %d)" % len(common_fnames))
        converter_fpaths = list(map(lambda x: os.path.join(converter_basedir, x), sorted(list(common_fnames))))
        original_fpaths = list(map(lambda x: os.path.join(original_basedir, x), sorted(list(common_fnames))))

    num_global_issues = 0
    for converter_fpath, original_fpath in zip(converter_fpaths, original_fpaths):
        num_local_issues = 0
        # num_skipped_lines = 0
        print("Starting analysis for files %s and %s" % (converter_fpath, original_fpath))
        with open(converter_fpath) as converter_f:
            converted_lines = converter_f.readlines()
        with open(original_fpath) as original_f:
            original_lines = original_f.readlines()
        if type == 'gene':
            if converted_lines[0] != original_lines[0]:
                __print_msg("Error! Files' headers do not match (%s vs %s)" % (converted_lines[0], original_lines[0]),
                            exit=args.stop_on_error)
                num_local_issues += 1
            converted_lines = converted_lines[1:]
            original_lines = original_lines[1:]
            if __gene_lists_diff(converted_lines, original_lines, converter_fpath, original_fpath, args.stop_on_error):
                num_local_issues += 1
        if num_local_issues:
            print("Done! Files %s and %s are NOT equivalent (see %d issues above)\n" %
                  (converter_fpath, original_fpath, num_local_issues))
        else:
            print("Done! Files %s and %s are equivalent\n" % (converter_fpath, original_fpath))
        num_global_issues += num_local_issues

    if len(converter_fpaths) > 1:
        if num_global_issues:
            print("Done! Dirs %s and %s are NOT equivalent (see %d issues above)" %
                  (args.converter_out, args.original_out, num_global_issues))
        else:
            print("Done! Dirs %s and %s are equivalent" % (args.converter_out, args.original_out))
    if num_global_issues:
        return 1
    return 0


if __name__ == "__main__":
    main()
