#!/usr/bin/env python
#
#
# Run the ns test cases multiple times, collect the outputs in per-testcase
# directories, and calculate means and stdev. Plot the means of scores. If -v
# is specified, then also plot stdev as variation bands.

from __future__ import print_function
import sys, os, time, subprocess, getopt, re, glob

def propVal(prop, propsFile):
    pattern = re.compile(prop + ":(.*)", re.IGNORECASE)
    for i, line in enumerate(open(propsFile)):
        for match in re.finditer(pattern, line):
            return match.group(1).strip()

allScores = "intact_score,hpc_score,acc_score"
        
progname=''
def usage():
    print('Usage: ' + progname + ' [-h] [-t trace_level] [-b] [-s] [-r] [-f] ' + 
          '[-d outdir] [-p propsPat] [-v] [-y penalty] [-X xrange] ' +
          '[-Y yrange] [numruns]')
    print('  -h: Print this message')
    print('  -t: trace level passed to ns')
    print('  -b: big plot')
    print('  -s: small plot with no legend')
    print('  -r: recalculate avg, stdev and sterr (only with numruns == 0)')
    print('  -f: plot to file(s) outdir/testcase.svg (default plotdir = outdir)')
    print('  -d: output base directory. Default is out/yyyy_mm_dd__hh_mm_ss')
    print('        Subdirectories will be created for each test case')
    print('  -p: propsPat[s] to run (default: all of them)')
    print('  -v: plot variation (stdev) bands')
    print('  -y: penalty for extra active units')
    print('  -X: x range for plotting, e.g. [0:60]')
    print('  -Y: y range for plotting, e.g. [0:1.2]')
    print('  <numRuns>: number of runs of each test case. Default is 0')
    print('        If <numRuns> == 0, then plot existing data in <dir>')

    sys.exit(2)

def main():
    progname = os.path.basename(sys.argv[0])
    try:
        opts, args = getopt.getopt(sys.argv[1:], "ht:bsrfd:p:X:Y:vy:",
                                   ["help", "tl=", "big", "small", "recalc",
                                    "file", "dir=", "propsPat=",
                                    "vbands", "penalty"])
    except getopt.GetoptError as err:
        print(err)
        usage()
        sys.exit(2)

    small = False
    big = False
    recalc = False
    plotToFile=False
    propsPatterns = []
    outBaseDir = ''
    vflag = ' '
    Xflag = ' '
    Yflag = ' -Y [0:1.2] '
    numRuns = 0
    extraPenalty="0.5"
    nsArgs=[]
    
    for opt, val in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit()
        elif opt in ("-t", "--tl"):
            nsArgs.append("-tl")
            nsArgs.append(val)
            
        elif opt in ("-b", "--big"):
            big = True
        elif opt in ("-s", "--small"):
            small = True
        elif opt in ("-r", "--recalc"):
            recalc = True
        elif opt in ("-f", "--file"):
            plotToFile = True
        elif opt in ("-d", "--dir"):
            outBaseDir = val
        elif opt in ("-p", "--propsPat"):
            propsPatterns = propsPatterns + [val]
        elif opt in ("-v", "--vbands"):
            vflag = " -v"
        elif opt in ("-X", "--xrange"):
            Xflag = " -X " + val
        elif opt in ("-Y", "--yrange"):
            Yflag = " -Y " + val
        elif opt in ("-y", "--penalty"):
            extraPenalty = val
        else:
            assert False, "unhandled option"

    if (big and small):
        print('Big and small?')
        sys.exit(2)

    if (big):
        plotFlags="-w 1560 -h 975 -c ns.col -k off" + vflag + Xflag + Yflag
    elif (small):
        plotFlags="-w 280 -h 180 -c ns.col -k off" + vflag  + Xflag + Yflag
    else:
        plotFlags="-w 450 -h 300 -c ns.col" + vflag  + Xflag + Yflag

    # Extracts args containing '=' into a separate list to be passed to ns
    #
    for a in args:
        if "=" in a:
            nsArgs.append(a)
    for a in nsArgs:
        if a in args:
            args.remove(a)
    nsArgs = ' '.join(nsArgs)

    # If there's exactly one arg left, then it must be numRuns
    #
    if len(args) > 1:
        print('Too many arguments')
        usage()
        sys.exit(2)
    elif (len(args) == 1):
        try:
            numRuns = int(args[0])
        except:
            print('Bad value for numRuns: ' + args[0]);
            sys.exit(2)

    propsPathNames = []
    for pp in propsPatterns:
        propsPathNames += glob.glob('props/*' + pp + '*.props')

    if (len(propsPathNames) == 0):
        propsPathNames = glob.glob('props/*.props')
        propsPathNames.sort()

    if (numRuns == 0):
        if (outBaseDir == ''):
            outBaseDir = 'lastout'
        if (not os.path.isdir(outBaseDir)):
            print("'" + outBaseDir +
                  "' is not a directory (must exist when numRuns == 0)")
            sys.exit(2)
    else:
        if (outBaseDir == ''):
            outBaseDir = 'out' + '/' + time.strftime('%Y_%m_%d__%H_%M_%S')

        if (os.path.isdir(outBaseDir)):
            print("'" + outBaseDir +
                  "' already exists (not ok when numRuns != 0)")
            sys.exit(2)

        os.system ("ln -sfT " + outBaseDir + " lastout")
        
    for propsPath in propsPathNames:
        # print("---- " + propsPath)
        # extract the props file base name
        m = re.match('props/(.*)\.props$', propsPath)
        if m:
            pname = m.group(1)
        else:
            print("What the heck?")
            sys.exit(2)

        # extract test case ID from base name
        m = re.match('ns_([^_]*)', pname)
        if m:
            tcId = '[' + m.group(1) + ']'
        else:
            tcId = ''

        outDir = outBaseDir + '/' + pname
        avgFile = outDir + '/' + 'avg.out'
        stdevsFile = outDir + '/' + 'stdevs.out'
        sterrFile = outDir + '/' + 'sterr.out'
        statsFile = outDir + '/' + 'stats.out'
        plotFile = outBaseDir + '/' + pname + ".svg"
        # print("propsPath = " + propsPath)
        title = tcId + ' ' + propVal("title", propsPath)
        
        if (numRuns != 0):
            os.makedirs(outDir)
            procs = []
            
            # Run numRuns copies of ns with propsPath in parallel
            #
            for i in range(numRuns):
                print(i)
                rawFile = outDir + '/' + str(i) + '.raw'
                cmd = ("ns " + nsArgs + " " + propsPath + " > " + rawFile)
                p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
                                     stderr=subprocess.STDOUT)
                procs.append(p)

            exitCodes = [p.wait() for p in procs]
            print('Exit codes(' + propsPath + '): ', end='')
            print(exitCodes)

            outputs = [p.communicate()[0] for p in procs]
            print('Outputs(' + propsPath + '): ', end='')
            print(outputs)

            # Post-process: extract data from the [i].raw ns output file,
            # into files named [i].intact, [i].hpc, [i].acc, then combine
            # these into [i].out
            #
            for i in range(numRuns):
                rawFile = outDir + '/' + str(i) + '.raw'
                intactFile = outDir + '/' + str(i) + '.intact'
                hpcFile = outDir + '/' + str(i) + '.hpc'
                accFile = outDir + '/' + str(i) + '.acc'
                outFile = outDir + '/' + str(i) + '.out'

                os.system("echo time intact_hits intact_extras intact_score" +
                          "> " + intactFile)
                os.system("awk 'BEGIN {p = " + extraPenalty + "} "
                          "/intact-settled.*SC1/ "
                          "{score=($6-p*$7)/$5; "
                          "if (score<0) score = 0; "
                          "print $1,$6,$7,score}' " +
                          rawFile + " >> " + intactFile)

                os.system("echo hpc_hits hpc_extras hpc_score > " + hpcFile)
                os.system("awk 'BEGIN {p = " + extraPenalty + "} "
                          "/hpc-frozen-settled.*SC1/ "
                          "{score=($6-p*$7)/$5; "
                          "if (score<0) score = 0; "
                          "print $6,$7,score}' " +
                          rawFile + " >> " + hpcFile)

                os.system("echo acc_hits acc_extras acc_score > " + accFile)
                os.system("awk 'BEGIN {p = " + extraPenalty + "} "
                          "/acc-frozen-settled.*SC1/ "
                          "{score=($6-p*$7)/$5; "
                          "if (score<0) score = 0; "
                          "print $6,$7,score}' " +
                          rawFile + " >> " + accFile)

                os.system("paste " + intactFile + " " + hpcFile + " " +
                          accFile + " > " + outFile)

        if ((numRuns != 0) or recalc):
            procs = []

            cmd = 'mat -hdr -ind avg ' + outDir + '/[0-9]*.out > ' + avgFile
            p = subprocess.Popen(cmd, shell=True)
            procs.append(p)

            cmd = ('mat -hdr -ind -pref S_ stdevs ' + outDir +
                   '/[0-9]*.out > ' + stdevsFile)
            p = subprocess.Popen(cmd, shell=True)
            procs.append(p)

            cmd = ('mat -hdr -ind -pref E_ sterr ' + outDir +
                   '/[0-9]*.out > ' + sterrFile)
            p = subprocess.Popen(cmd, shell=True)
            procs.append(p)

            exitCodes = [p.wait() for p in procs]

            cmd = ("paste " + avgFile + " " + stdevsFile + " " +
                   sterrFile + " | " +
                   " columns time   intact_hits   intact_extras intact_score"
                   "                   hpc_hits      hpc_extras    hpc_score"
                   "                   acc_hits      acc_extras    acc_score "
                   "              S_intact_hits S_intact_extras S_intact_score"
                   "                 S_hpc_hits    S_hpc_extras    S_hpc_score"
                   "                 S_acc_hits    S_acc_extras    S_acc_score "
                   "              E_intact_hits E_intact_extras E_intact_score"
                   "                  E_hpc_hits   E_hpc_extras    E_hpc_score"
                   "                  E_acc_hits   E_acc_extras    E_acc_score "
                   " > " + statsFile)
            p = subprocess.Popen(cmd, shell=True)
            p.wait()

        if (plotToFile):
            outputOption = " -o " + plotFile
        else:
            outputOption = ""
            print(title)
        cmd = ("nsplot " + plotFlags + outputOption +
               " -s " + allScores +
               " -t " + '"' + title +
               '" < ' + statsFile)
        p = subprocess.Popen(cmd, shell=True)
        p.wait()
        print(cmd)

if __name__ == "__main__":
    main()