#!/usr/bin/env python3
'''
Convert a FAVITES contact network and transmission network to the GEXF format.
'''
from time import strftime

# convert a FAVITES transmission network to the GEXF format
def favites2gexf(cn,tn):
    # parse transmission network
    trans = []
    infections_to = dict()
    for line in tn:
        if isinstance(line,bytes):
            l = line.decode().strip()
        else:
            l = line.strip()
        if len(l) == 0 or l[0] == '#':
            continue
        try:
            u,v,t = l.split(); t = float(t)
        except:
            assert False, "Transmission network is not in the FAVITES format"
        trans.append((u,v,t))
        if v not in infections_to:
            infections_to[v] = []
        infections_to[v].append((u,v,t))
    END_TIME = trans[-1][2]

    # parse contact network
    edges = dict()
    nodes = set()
    for line in cn:
        if isinstance(line,bytes):
            l = line.decode().strip()
        else:
            l = line.strip()
        if len(l) == 0 or l[0] == '#':
            continue
        assert l.startswith('NODE\t') or l.startswith('EDGE\t'), "Contact network is not in the FAVITES format"
        if l.startswith('NODE\t'):
            continue
        try:
            n,u,v,a,d = l.split()
        except:
            assert False, "Contact network is not in the FAVITES format"
        nodes.add(u); nodes.add(v)
        edges[(u,v)] = [0,END_TIME+1]
        if d == 'u': # undirected
            edges[(v,u)] = [0,END_TIME+1]
        if u not in infections_to:
            infections_to[u] = []
        if v not in infections_to:
            infections_to[v] = []
    nodes = sorted(nodes)

    # set up edges for conversion to GEXF
    for u,v,t in tn:
        if u is not None and u != v:
            try:
                times = edges[(u,v)]
            except KeyError:
                raise KeyError("(%s,%s)" % (str(u),str(v)))
            for i in range(len(times)):
                if times[i] > t:
                    break
            edges[(u,v)] = times[:i] + [t] + times[i:]

    # convert to GEXF
    out = '<?xml version="1.0" encoding="UTF-8"?>\n'
    out += '<gexf xmlns="http://www.gexf.net/1.3" version="1.3" xmlns:viz="http://www.gexf.net/1.3/viz" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/1.3 http://www.gexf.net/1.3/gexf.xsd">\n'
    out += '  <meta lastmodifieddate="%s">\n' % strftime("%Y/%m/%d")
    out += '    <creator>FAVITES</creator>\n'
    out += '    <description>Transmission network generated by FAVITES (Niema Moshiri 2017)</description>\n'
    out += '  </meta>\n'
    out += '  <graph defaultedgetype="directed" timeformat="double" timerepresentation="interval" mode="dynamic">\n'
    out += '    <attributes class="node" mode="dynamic">\n'
    out += '      <attribute id="infected" title="infected" type="boolean" />\n'
    out += '    </attributes>\n'
    out += '    <attributes class="edge" mode="dynamic">\n'
    out += '      <attribute id="transmission" title="transmission" type="boolean" />\n'
    out += '    </attributes>\n'
    out += '    <nodes>\n'
    for node in nodes:
        times = [t for u,v,t in infections_to[node]]
        out += '      <node id="%s" label="%s">\n' % (node,node)
        out += '        <spells>\n'
        out += '          <spell start="0.0" />\n'
        out += '        </spells>\n'
        out += '        <attvalues>\n'
        if len(times) == 0: # uninfected nodes
            out += '          <attvalue for="infected" value="false" start="0.0" end="%f" />\n' % (END_TIME+1)
        elif times[0] == 0: # seed node
            out += '          <attvalue for="infected" value="true" start="0.0" end="%f" />\n' % (END_TIME+1)
        else: # regular infected nodes
            out += '          <attvalue for="infected" value="false" start="0.0" end="%f" />\n' % times[0]
            out += '          <attvalue for="infected" value="true" start="%f" end="%f" />\n' % (times[0],END_TIME+1)
        out += '        </attvalues>\n'
        out += '      </node>\n'
    out += '    </nodes>\n'
    out += '    <edges>\n'
    edge_count = 0
    for u,v in edges:
        times = edges[(u,v)]
        out += '      <edge id="%d" source="%s" target="%s">\n' % (edge_count,u,v)
        edge_count += 1
        out += '        <spells>\n'
        out += '          <spell start="0.0" />\n'
        out += '        </spells>\n'
        out += '        <attvalues>\n'
        for i in range(len(times)-1):
            out += '          <attvalue for="transmission" value="%s" start="%f" end="%f" />\n' % (str(i != 0).lower(),times[i],times[i+1])
        out += '        </attvalues>\n'
        out += '      </edge>\n'
    out += '    </edges>\n'
    out += '  </graph>\n'
    out += '</gexf>\n'
    return out

from gzip import open as gopen
from sys import stdout
import argparse
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-c', '--contact_network', required=True, type=str, help="FAVITES contact network")
parser.add_argument('-t', '--transmission_network', required=True, type=str, help="FAVITES transmission network")
parser.add_argument('-o', '--output', required=False, default='stdout', type=str, help="Output File")
args,unknown = parser.parse_known_args()
if args.output == 'stdout':
    args.output = stdout
else:
    args.output = open(args.output,'w')
if args.contact_network.lower().endswith('.gz'):
    args.contact_network = gopen(args.contact_network)
else:
    args.contact_network = open(args.contact_network)
if args.transmission_network.lower().endswith('.gz'):
    args.transmission_network = gopen(args.transmission_network)
else:
    args.transmission_network = open(args.transmission_network)
args.output.write(favites2gexf(args.contact_network,args.transmission_network))
