"""
Compare multiple GFF files to a reference
"""

import os
import pandas as pd

from mirtop.gff import header
from mirtop.gff.body import read_attributes, read_gff_line
from mirtop.mirna.realign import read_id
import mirtop.libs.logger as mylog
logger = mylog.getLogger(__name__)

# Add check first 

def compare(args):
    """
    From a list of files produce stats
    """
    out = list()
    result = dict()
    reference = read_reference(args.files[0])
    for fn in args.files[1:]:
        if not os.path.exists(fn):
            raise IOError("%s doesn't exist" %s)
        result[os.path.basename(fn)] = _compare_to_reference(fn, reference)
    if args.out != "tmp_mirtop":
        fn_out = os.path.join(args.out, "summary.txt")
        with open(fn_out, 'w') as outh:
            for fn in result:
                print >>outh,  "sample\tidu\tseq\ttag\tsame_mirna\t%s" % "\t".join(result[fn][0][3].keys())
                for line in result[fn]:
                    read = read_id(line[0])
                    acc = "\t".join([line[3][v] for v in line[3]])
                    print >>outh, "%s\t%s\t%s\t%s\t%s\t%s" % (fn, line[0], read, line[1], line[2], acc)

def read_reference(fn):
    """Read GFF into UID:Variant key:value dict"""
    srna = dict()
    with open(fn) as inh:
        for line in inh:
            if line.startswith("#"):
                continue
            cols = read_gff_line(line)
            attr = cols['attrb']
            srna[attr['UID']] = [_simplify(attr['Variant']), attr]
    return srna

def _compare_to_reference(fn, reference):
    same = 0
    diff = list()
    extra = list()
    miss = list()
    results = list()
    seen = 0
    seen_reference = set()
    with open(fn) as inh:
        for line in inh:
            if line.startswith("#"):
                continue
            cols = read_gff_line(line)
            attr = cols['attrb']
            if attr['UID'] in reference:
                mirna = "Y" if attr['Name'] == reference[attr['UID']][1]['Name'] else attr['Name']
                accuracy =  _accuracy(_simplify(attr['Variant']), reference[attr['UID']][0])
                results.append([attr['UID'], "D", mirna, accuracy])
                if _simplify(attr['Variant']) == reference[attr['UID']][0]:
                    same += 1
                else:
                    diff.append("%s | reference: %s" % (line.strip(), reference[attr['UID']][1]))
                seen += 1
                seen_reference.add(attr['UID'])
            else:
                extra.append("%s | extra" % line.strip())
                results.append([attr['UID'], "E", attr['Name'], _accuracy(_simplify(attr['Variant']), "")])
    for uid in reference:
        if uid not in seen_reference:
            results.append([uid, "M", "N", _accuracy("", reference[uid][0])])
            miss.append("| miss %s" %  reference[uid][1])
    logger.info("Number of sequences found in reference: %s" % seen)
    logger.info("Number of sequences matches reference: %s" % same)
    logger.info("Number of sequences different than reference: %s" % len(diff))
    logger.info("Number of sequences extra sequences: %s" % len(extra))
    logger.info("Number of sequences missed sequences: %s" % len(miss))
    return results

def _simplify(variant):
    simple = [v.split(":")[0] for v in variant.split(",")]
    return ",".join(simple)

def _get_samples(fn):
    with open(fn) as inh:
        for line in inh:
            if line.startswith("## COLDATA"):
                return line.strip().split(": ")[1].strip().split(",")
    raise ValueError("%s doesn't contain COLDATA header." % fn)

def _accuracy(target, reference):
    """Compare each isomir label in Variant field
       and return a list with values whether:
           FP: no in reference
           FN: no in target
           TP: same values
    """
    logger.debug("COMPARE::ACCURACY::values %s vs %s" % (target, reference))
    accuracy = dict()
    types =  ["iso_5p", "iso_3p", "iso_add", "iso_snp",
              "iso_snp_seed", "iso_snp_central",
              "iso_snp_central_supp", "iso_snp_central_offset"]
    for t in types:
        if t in reference:
            accuracy[t] = "TP" if t in target else "FN"
        else:
            accuracy[t] = "TN" if t not in target else "FP"
    logger.debug("COMPARE::ACCURACY::%s" % accuracy.keys())
    logger.debug("COMPARE::ACCURACY::%s" % accuracy.values())
    return accuracy
