#!/usr/bin/python
import tempfile
import datetime
import subprocess
import shutil
import os
import os.path
import time

class Data:
    def __init__(self, build, date, data):
        self.build=build
        self.date=date
        self.data=data
    def __str__(self):
        return ",".join([str(self.build),
                         str(self.date),
                         str(self.data)])
    def __repr__(self):
        return ", ".join([repr(self.build),
                         repr(self.date),
                         repr(self.data)])
class Plot:
    def __init__(self, name, descr, values, norm_name, range=None):
        self.values=values
        self.norm_name=norm_name
        self.data={}
        self.name=name
        self.descr=descr
        if range:
            self.range=range
    def __str__(self):
        return ", ".join([str(self.values),
                         self.norm_name,
                         str(self.data),
                         self.name])
    def get_description(self):
        return self.descr
    def get_name(self):
        return self.name
    def add_data(self,passdata):
        date= passdata.date
        data={}
        for v in self.values:
            if v in passdata.data.keys():
                data[v]=passdata.data[v]
        self.data[date]=data
    def get_values(self):
        return self.values
    def get_norm(self):
        dates= self.data.keys()
        dates.sort
        for d in dates:
            if self.norm_name in self.data[d].keys():
                return self.data[d][self.norm_name]
    def get_tabular_data(self):
        dates= self.data.keys()
        dates.sort()
        ret=[]
        norm= self.get_norm()
        for d in dates:
            line=[ str(d.month)+"/"+str(d.day)+"/"+str(d.year) ]
            has_data=False
            for v in self.values:
                if v in self.data[d].keys():
                    #print self.data[d][v], float(self.data[d][v]), float(norm)
                    line.append(float(self.data[d][v])/float(norm))
                    has_data=True
                else:
                    line.append("")
            if has_data:
                ret.append(line)
        return ret
    def get_y_range(self):
        try:
            return (0, self.range)
        except:
            min=1000000
            max=0
            norm= self.get_norm()
            for d in self.data.keys():
                for v in self.values:
                    if v in self.data[d].keys():
                        val=float(self.data[d][v])/float(norm)
                        if val < min:
                            min=val
                        if val > max:
                            max=val
            return (0, max)


plots=[Plot("small_xyz", "These benchmarks show the relative cost of iterating through a small set of coordinates in various ways.",
            ["xyz decorator small",
             "xyz particle small",
             "xyz decorator from index small",
             "xyz vector small",
             "xyz vector space small"], "xyz vector small", range=4),
       Plot("large_xyz","These benchmarks show the relative cost of iterating through a large set of coordinates in various ways.",
            ["xyz decorator large",
             "xyz particle large",
             "xyz decorator from index large",
             "xyz vector large",
             "xyz vector space large"], "xyz vector large", range=4),
       Plot("huge_xyz","These benchmarks show the relative cost of iterating through a huge set of coordinates in various ways.",
            ["xyz decorator huge",
             "xyz particle huge",
             "xyz decorator from index huge",
             "xyz vector huge",
             "xyz vector space huge"], "xyz vector huge"),
       Plot("rotation","These benchmarks show the relative cost of computing rotations directory from the quaternion and from a cached rotation matrix.",
            ["rotation (cache)",
             "rotation (nocache)"], "rotation (nocache)"),
       Plot("rigid_collision_detection", "These benchmarks compare quadratic collision detection on two rigid bodies to using the sphere hierarchy.",
            ["rigid quadratic 10",
             "rigid quadratic 30",
             "rigid hierarchy 10",
             "rigid hierarchy 30"], "rigid quadratic 10"),
       Plot("refiners", "These benchmarks compare the cost of getting the children of a hierarchy node to getting all leaves.",
            ["refiner children",
             "refiner leaves"], "refiner leaves"),
       Plot("collision_detection", "These benchmarks compare various methods of collision detection.",
            ["col quadratic 10000 0.1",
             "col quadratic 10000 0.5",
             "col box 10000 0.1",
             "col box 10000 0.5",
             "col grid 10000 0.1",
             "col grid 10000 0.5"], "col quadratic 10000 0.1"),
       Plot("nearest_neighbors", "These benchmarks compare various methods for computing nearest neighbors on 10 particles.",
            ["knn cgal uniform 10 0",
             "knn cgal uniform 10 0.1",
             "knn cgal uniform 10 0.5",
             "knn linear uniform 10 0",
             "knn linear uniform 10 0.1",
             "knn linear uniform 10 0.5",], "knn cgal uniform 10 0"),
       Plot("nearest_neighbors_medium", "These benchmarks compare various methods for computing nearest neighbors on 100 particles.",
             ["knn cgal uniform 100 0",
             "knn cgal uniform 100 0.1",
             "knn cgal uniform 100 0.5",
             "knn linear uniform 100 0",
             "knn linear uniform 100 0.1",
             "knn linear uniform 100 0.5"], "knn cgal uniform 100 0"),
       Plot("nearest_neighbors_large", "These benchmarks compare various methods for computing nearest neighbors on 1000 particles.",
             ["knn cgal uniform 1000 0",
             "knn cgal uniform 1000 0.1",
             "knn cgal uniform 1000 0.5",
             "knn linear uniform 1000 0",
             "knn linear uniform 1000 0.1",
             "knn linear uniform 1000 0.5"], "knn cgal uniform 1000 0"),
       Plot("container_access", "These benchmarks compare the costs of access various types of containers.",
            ["container direct list in 4950",
             "container ssps direct list in 4950",
             "container ssps direct call list in 4950",
             "container ssps direct bind list in 4950",
             "container direct bind list in 4950",
             "container list in 4950",
             "container list out 4950",
             "container direct set in 2450",
             "container ssps direct set in 2450",
             "container ssps direct call set in 2450",
             "container ssps direct bind set in 2450",
             "container direct bind set in 2450",
             "container set in 2450",
             "container set out 2450"], "container direct list in 4950"),
       Plot("container_access_large", "These benchmarks compare the costs of access various types of containers.",
            ["container direct list in 499500",
             "container ssps direct list in 499500",
             "container ssps direct call list in 499500",
             "container ssps direct bind list in 499500",
             "container direct bind list in 499500",
             "container list in 499500",
             "container list out 499500",
             "container direct set in 249500",
             "container ssps direct set in 249500",
             "container ssps direct call set in 249500",
             "container ssps direct bind set in 249500",
             "container direct bind set in 249500",
             "container set in 249500",
             "container set out 249500"], "container direct list in 499500"),
       #Plot("density_map_access","These benchmarks compare the costs of computing the centers of cells of a density map to looking them up in a table.",
       #     ["uncached density loop",
       #      "cached density loop"], "uncached density loop"),
       Plot("connectivity", "These benchmarks compare the costs of computing connectivity in various ways.",
            ["connectivity slow",
             "connectivity fast",
             "connectivity fast mst"], "connectivity slow"),
       Plot("close_pairs_finders_small","These benchmarks compare the various close pairs finders.",
            ["cpf grid 1000 0.1",
             "cpf grid 1000 0.5",
             "cpf nn 1000 0.1",
             "bcpf nn 1000 0.1",
             "cpf nn 1000 0.5",
             "bcpf nn 1000 0.5",
             "cpf box 1000 0.1",
             "bcpf box 1000 0.1",
             "cpf box 1000 0.5",
             "bcpf box 1000 0.5",
             "cpf quadratic 1000 0.1",
             "bcpf quadratic 1000 0.1",
             "cpf quadratic 1000 0.5",
             "bcpf quadratic 1000 0.5"], "cpf grid 1000 0.1"),
       Plot("close_pairs_finders_small_many","These benchmarks compare the various close pairs finders with many close pairs.",
            ["cpf grid 1000 5",
             "cpf nn 1000 5",
             "bcpf nn 1000 5",
             "cpf box 1000 5",
             "bcpf box 1000 5",
             "cpf quadratic 1000 5",
             "bcpf quadratic 1000 5"], "cpf grid 1000 5"),
       Plot("close_pairs_finders","These benchmarks compare the various close pairs finders.",
            ["cpf grid 10000 0.1",
             "cpf grid 10000 0.5",
             "cpf nn 10000 0.1",
             "bcpf nn 10000 0.1",
             "cpf nn 10000 0.5",
             "bcpf nn 10000 0.5",
             "cpf box 10000 0.1",
             "bcpf box 10000 0.1",
             "cpf box 10000 0.5",
             "bcpf box 10000 0.5",], "cpf grid 10000 0.1"),
       Plot("close_pairs_finders_big","These benchmarks compare the various close pairs finders when there are many collisions.",
            ["cpf grid 10000 5",
             "cpf box 10000 5",
             "bcpf box 10000 5",
             "cpf quadratic 10000 5",
             "bcpf quadratic 10000 5"], "cpf grid 10000 5"),
       Plot("brownian_dynamics", "These benchmarks look at various optimizations of brownian dynamics.",
            ["bd custom",
             "bd scores",
             "bd generic"], "bd custom"),
       Plot("pdb", "Reading pdb files", ["pdb"], "pdb"),
       Plot("excluded_volume", "Various approacches for computing excluded volume between rigid bodies",
            ["k close random",
             "k close systematic",
             "k close far",
             "k close close",
             "k close random if good",
             "k close systematic if good",
             "k close far if good",
             "k close close if good",
             "excluded volume random",
             "excluded volume systematic",
             "excluded volume far",
             "excluded volume close",
             "excluded volume random if good",
             "excluded volume systematic if good",
             "excluded volume far if good",
             "excluded volume close if good",
             "pairs restraint random",
             "pairs restraint systematic",
             "pairs restraint far",
             "pairs restraint close",
             "pairs restraint random if good",
             "pairs restraint systematic if good",
             "pairs restraint far if good",
             "pairs restraint close if good"], "excluded volume far")

       ]
def data_from_text(text):
    ret=[]
    curdata={}
    curmode=None
    for l in text:
        try:
            if l[0]=="date":
                dt= l[1].split("/")
                curdate=datetime.date(int(dt[2]), int(dt[0]), int(dt[1]))
            elif l[0]=="mode":
                if curdata != {} and curmode == "fast":
                    ret.append(Data(curmode, curdate, curdata))
                curmode=l[1]
                curdata={}
            else:
                curdata[l[0]]=l[1]
        except:
            print "Error on line", l
            exit(1)
    return ret

def add_data_to_plots(plots, data):
    for p in plots:
        for d in data:
            p.add_data(d)

def text_from_file(fn):
    all= open(fn, "r").read()
    lines=[x for x in all.splitlines() if x is not ""]
    data=[x.split(",") for x in lines]
    return data


def write_driver(fn, f, title, name, names, yrange, log=False):
    lines=[]
    lines.append("set terminal png size 640, 480")
    lines.append('set output "%s"'%(name+".png"))

    lines.append("set xdata time")
    lines.append('set timefmt "%m/%d/%y"')
    if log:
        lines.append( "set logscale y")
    lines.append('set datafile separator ","')
    #lines.append("set xtics '6/1/2007',31536000")
    lines.append('set yrange ['+str(yrange[0])+':'+str(yrange[1])+']')
    first=True
    verb='plot'
    for i in range(0, len(names)):
        lines.append(verb+' "'+fn+'" using 1:'+str(i+2)\
            + ' with linespoints title "' + names[i] +'"\n')
        if first:
            verb='replot'

    lines.append("set terminal png size 2000, 1000")
    lines.append('set output "%s"'%("benchmark_"+name+".png"))
    lines.append('replot')
    f.write("\n".join(lines)+"\n")


def main():
    textlines= text_from_file('modules/benchmark/doc/benchmark_results.csv')
    #print textlines
    data=data_from_text(textlines)
    #print data
    add_data_to_plots(plots, data)
    page=[]
    page.append("/** \\page  benchmarkr Benchmark results")
    files=[]
    for p in plots:
        #print str(p)
        print p.get_description()
        tf=tempfile.NamedTemporaryFile(suffix=".csv", delete=False)
        pd=p.get_tabular_data()
        tf.write("\n".join([", ".join([str(y) for y in x]) for x in pd]))
        df= tempfile.NamedTemporaryFile(suffix="gnuplot", delete=False)
        #print p.get_name(), tf.name, df.name
        write_driver(tf.name, df, p.get_name(), p.get_name(), p.get_values(), p.get_y_range())
        tf.close()
        dfn= df.name
        print "gnuplot", dfn
        df.close()
        #ret=subprocess.call(["gnuplot", dfn])
        sp=subprocess.Popen(["gnuplot "+ dfn], shell=True, stderr=subprocess.STDOUT)
        ret=sp.wait()
        if ret != 0:
            print "Error running gnuplot"
        #time.sleep(2)
        files.append("benchmark_"+p.get_name()+".png")
        page.append("\\section "+p.get_name()+" "+p.get_name().replace("_", " "))
        page.append(p.get_description())
        page.append("")
        page.append("\\inlineimage{benchmark_"+p.get_name()+".png, 480}\n")
    page.append("*/")
    open("modules/benchmark/doc/results.dox", "w").write("\n".join(page))
    for f in files:
        try:
            os.unlink("modules/benchmark/doc/"+f)
        except:
            print f, "does not exist"
            pass
        print "trying", f
        while not os.path.isfile(f):
            time.sleep(1)
        shutil.move(f, "modules/benchmark/doc/"+f)
if __name__ == '__main__':
    main()
