#!/usr/bin/env python
#
# from venge.net/monotone
#


import sys
import csv
import glob
import time
import os
import os.path
import datetime

os.environ["TTFPATH"] = ":".join(["/usr/share/fonts/truetype/" + d
                                  for d in "ttf-bitstream-vera",
                                           "freefont",
                                           "msttcorefonts"])
import matplotlib
matplotlib.use("Agg")
import matplotlib.pylab as m

def main(progname, args):
    if len(args) != 2:
        sys.exit("Usage: %s DATADIR OUTDIR" % progname)

    branch = "trunk"
    datadir, outdir = args

    # First, load in all data from the data directory.
    data = []
    for datapath in glob.glob(os.path.join(datadir, "*.csv")):
        data.append(read_csv(datapath))
    # Sort by time
    data.sort()

    # Calculate time series for each file.
    times = [sample[0] for sample in data]
    times = [datetime.datetime.utcfromtimestamp(t) for t in times]
    times = m.date2num(times)
    all_files = {}
    for sample in data:
        t, i, tot_line, tot_cover, per_file = sample
        all_files.update(per_file)
    total_series = []
    file_serieses = dict([[k, [(0, 0)] * len(times)] for k in all_files.keys()])
    data_idx = 0
    for sample in data:
        t, i, tot_line, tot_cover, per_file = sample
        total_series.append([tot_line, tot_cover])
        for f, covinfo in per_file.items():
            file_serieses[f][data_idx] = covinfo
        data_idx += 1

    # Okay, ready to start outputting.  First make sure our directories
    # exist.
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    rel_imgdir = "images"
    imgdir = os.path.join(outdir, rel_imgdir)
    if not os.path.exists(imgdir):
        os.makedirs(imgdir)
    rel_annotateddir = os.path.join("..", "data")
    
    # Now plot the actual graphs
    plot_files = {}
    plot_files["Total"] = plot_coverage(times, total_series, imgdir, "Total")
    for f, series in file_serieses.items():
        plot_files[f] = plot_coverage(times, series, imgdir, f)
    
    # Now sort the files by the total quantity of uncovered lines, decreasing,
    # breaking ties by total quantity of lines, decreasing.
    files_sorted_tmp = []
    for f, series in file_serieses.items():
        last_tot, last_cov = series[-1]
        files_sorted_tmp.append([last_cov - last_tot, -last_tot, f])
    files_sorted_tmp.sort()
    files_sorted = [row[-1] for row in files_sorted_tmp]

    # And look up the latest revision id, and coverage information
    last_time, last_id, last_tot_lines, last_tot_covered = data[-1][:4]

    # Now start generating our html file
    index = open(os.path.join(outdir, "index.html"), "w")
    index.write("""<HTML>
    <HEAD><TITLE>%s test coverage information</TITLE></HEAD>
    <BODY BGCOLOR="#ffffff"
    """   # " make emacs happier
                % "trunk")
    def sumcov(cov):
        return "%.2f%% (%s/%s)" % (cov[1] * 100.0 / (cov[0] or 1), cov[1], cov[0])
    index.write("""<H1>%s test coverage information</H1>
<UL><LI>Last updated: %s UTC</LI><LI>Latest head processed: %s</LI>
<LI>That head's coverage: %s</LI></UL>
<P>This is line coverage, generated by gcov.  If you know of a way to get
better numbers for C++ code, let <A HREF="njs@pobox.com">me know</A>.</P>
<P>Code is available via netsync venge.net, branch
org.vorpus.monotone-cov.</P>
<P>How to read annotated source: left column is number of times a statement was
executed.  "#####" means 'never executed'; that's bad.  Add some tests!</P>
""" # " make emacs happier
    % (branch, time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(last_time)),
       last_id, sumcov((last_tot_lines, last_tot_covered))))
    index.write("<H2>Breakdown</H2>")
    index.write("<UL>\n")
    index.write('<LI><A HREF="#total">Total</A>: %s</LI>\n'
                % sumcov((last_tot_lines, last_tot_covered)))
    for f in files_sorted:
        index.write('<LI><A HREF="#%s">%s</A>: %s</LI>\n'
                    % (f, f, sumcov(file_serieses[f][-1])))
    index.write("</UL>")

    index.write("<HR>")
    index.write('<P><A NAME="total"><IMG SRC="%s"></A></P>'
                % os.path.join(rel_imgdir, plot_files["Total"]))
    index.write('<P>Total coverage: %s</P>' % sumcov((last_tot_lines, last_tot_covered)))
    index.write('<P>Browse annotated source: <A HREF="%s">source</A></P>'
                % os.path.join(rel_annotateddir, last_id + ".annotated"))

    for f in files_sorted:
        index.write("<HR>\n")
        index.write('<P><A NAME="%s"><IMG SRC="%s"></A></P>\n'
                    % (f, os.path.join(rel_imgdir, plot_files[f])))
        index.write('<P>%s coverage: %s</P>\n' % (f, sumcov(file_serieses[f][-1])))
        index.write('<P>Annotated source: <A HREF="%s">source</A></P>\n'
                    % (os.path.join(rel_annotateddir, last_id + ".annotated", f)))

    index.close()

def read_csv(path):
    r = csv.reader(open(path, "r"))
    # First line is id, time
    for row in r:
        id, time_str = row
        break
    time = int(float(time_str))
    # Rest of lines are path, total_lines, covered_lines
    per_file = {}
    grand_total_lines, grand_covered_lines = 0, 0
    for row in r:
        path, total_lines_str, covered_lines_str = row
        total_lines = int(total_lines_str)
        covered_lines = int(covered_lines_str)
        grand_total_lines += total_lines
        grand_covered_lines += covered_lines
        per_file[path] = [total_lines, covered_lines]
    return [time, id, grand_total_lines, grand_covered_lines, per_file]


def plot_coverage(times, series, imgdir, name):
    percentages = [cov * 100.0 / (tot or 1) for tot, cov in series]
    m.plot_date(times, percentages, "b-")
    m.plot_date(times, percentages, "bo")
    m.title(name)
    m.ylim(0, 100)
    m.xlabel("Date")
    m.ylabel("Statement Coverage (%)")
    outfile_base = name.replace("/", "__") + ".png"
    outfile = os.path.join(imgdir, outfile_base)
    m.savefig(outfile, dpi=75)
    m.close()
    return outfile_base


if __name__ == "__main__":
    import sys
    main(sys.argv[0], sys.argv[1:])
