#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
input: file names of PD's protein xml and psms txt style yaml
output: protein group yaml for prophane
"""

import argparse
import yaml
import re
import xml.etree.ElementTree as ET
import sys

def parse_proteome_discoverer_output(xml_fname, psms_fname, style_fname, out_fname, decoy_regex=False):
    print("processing PD files ...")
    #process style info
    style = load_yaml(style_fname)

    #style checks
    mandatory_style_fields = ['xpath_pg', 'attr_protein_name', 'attr_pepseq', 'xpath_pep', 'fields_sep', 'seq_col', 'spectrum_file_col', 'n_prot_col', 'headline']
    missing_tags = [x for x in mandatory_style_fields if x not in style]
    if missing_tags:
        sys.exit('input style error in ' + style_fname + ":\nmissing information:\n" + ", ".join(missing_tags))

    #decoy
    if decoy_regex:
        decoy_regex = re.compile(decoy_regex)

    #processing xml file
    xml_root = ET.parse(xml_fname).getroot()
    proteins = []
    peptides = []
    i = -1
    for pg in xml_root.findall(style['xpath_pg'], style['namespaces']):
        i += 1
        p = sorted(set([x.attrib[style['attr_protein_name']] for x in pg.findall(style['xpath_ip'], style['namespaces'])]))
        if decoy_regex:
            p = [x for x in p if not decoy_regex.search(x)]
        if len(p) == 0:
            continue
        proteins.append(p)
        peptides.append([x.attrib[style['attr_pepseq']] for x in pg.findall(style['xpath_pep'], style['namespaces'])])

    #processing psms file
    fields_sep = style['fields_sep']
    pepseq_col = style['seq_col']
    sample_col = style['spectrum_file_col']
    n_prot_col = style['n_prot_col']
    pepseq_replace_pattern = re.compile(r'\[[A-Z]+\]\.|\.\[[A-Z]+\]')

    spectral_counts = {}
    with open(psms_fname, 'r') as handle:
        data_row = False
        for line in handle:
            line = line.strip()

            ##blank line
            if len(line) == 0:
                continue

            ##data rows
            elif data_row:
                fields = [x.strip('"') for x in line.split(fields_sep)]
                sample = fields[sample_col]
                if sample.endswith(".raw"):
                    sample = sample[:-4]
                pepseq = pepseq_replace_pattern.sub("", fields[pepseq_col].upper())
                n_prot = int(fields[n_prot_col])
                if n_prot == 0:
                    continue
                if sample not in spectral_counts:
                    spectral_counts[sample] = {}
                if pepseq not in spectral_counts[sample]:
                    spectral_counts[sample][pepseq] = 0
                spectral_counts[sample][pepseq] += 1

              ##headline
            elif line == style['headline']:
                data_row = True

    #error checks
    if not data_row:
        sys.exit("input format error: no style-specific headline in " + psms_fname)
    if len(proteins) == 0:
        sys.exit("input format error:\nno data rows " + xml_fname)
    if len(spectral_counts) == 0:
        sys.exit("input format error:\nno data rows in " + psms_fname)

    #output: protein_groups.yaml
    samples = sorted(spectral_counts.keys())
    yaml_data = {}

    ##samples
    yaml_data['samples'] = samples

    ##style definitions
    yaml_data['style'] = {}
    yaml_data['style']['protein_sep'] = style['protein_sep']
    yaml_data['style']['quant_type'] = style['quant_type']

    ##pgs
    yaml_data['protein_groups'] = []
    prots = set()
    for i in range(len(proteins)):
        yaml_data['protein_groups'].append({})
        yaml_data['protein_groups'][-1]['proteins'] = style['protein_sep'].join(proteins[i])
        prots.update(proteins[i])
        yaml_data['protein_groups'][-1]['quant'] = []
        for sample in samples:
            q = 0
            for pepseq in peptides[i]:
                if pepseq in spectral_counts[sample]:
                    q += spectral_counts[sample][pepseq]
            yaml_data['protein_groups'][-1]['quant'].append(q)

    ##writing
    with open(out_fname, "w") as handle:
        handle.write(yaml.dump(yaml_data, default_flow_style=False))

    ##output
    print("protein groups:", len(proteins))
    print("unique proteins:", len(prots))

def load_yaml(filename):
    '''reads data stored in a yaml file to a dict which is returned'''
    with open(filename, "r") as handle:
        return yaml.safe_load(handle)

def parse_args():
    parser = argparse.ArgumentParser(
        description="creates a protein group yaml for prophane based on scaffold's protein report and report style"
    )
    parser.add_argument('style', type=argparse.FileType('r'),
                    help='protein report style file (yaml)')
    parser.add_argument('xml', type=argparse.FileType('r'),
                        help='PD\'s protein xml export')
    parser.add_argument('psms', type=argparse.FileType('r'),
                        help='PD\'s psms txt export')
    parser.add_argument('out', type=argparse.FileType('w'),
                        help='prophane\'s config file (yaml)')
    parser.add_argument('-d', '--decoy', type=str, default=False,
                        help='regular expression matching to decoy accesions (will be excluded)')
    return parser.parse_args()


def main():
    args = parse_args()
    xml_fname = args.xml.name
    args.xml.close()
    psms_fname = args.psms.name
    args.psms.close()
    style_fname = args.style.name
    args.style.close()
    out_fname = args.out.name
    args.out.close()
    parse_proteome_discoverer_output(xml_fname, psms_fname, style_fname, out_fname, args.decoy)


if __name__ == "__main__":
    main()
