import os, math

preamble = "%!PS-Adobe-3.0 EPSF-3.0"

packetsPreamble = """
20 20 scale
0.5 0 translate
0.05 setlinewidth

% node time nodeTimeToXY x y
/nodeTimeToXY
{
    totalTime exch sub timeScale mul
} def

% n nodes -
/nodes
{
    0 1  3 -1 roll 1 sub
    {dup 0 nodeTimeToXY moveto totalTime nodeTimeToXY lineto stroke} for
} def

% intervalSecs length ticks -
/ticks
{
    gsave
    currentlinewidth .3 mul setlinewidth
    -0.5 0 translate
    0  2 index  totalTime
    {0 exch nodeTimeToXY moveto dup 0 rlineto stroke} for
    pop
    grestore
} def

% sendNode sendTime recvNode recvTime packet -
/packet
{
    gsave
    currentlinewidth .5 mul setlinewidth
    nodeTimeToXY moveto nodeTimeToXY lineto stroke
    grestore
} def"""

packetsPreamble = """
20 20 scale
0.05 setlinewidth

% node time nodeTimeToXY x y
/nodeTimeToXY
{
    timeScale mul exch totalNodes lineNo mul add
} def

% - nodes -
/nodes
{
    0 1  totalNodes 1 sub
    {dup 0 nodeTimeToXY moveto timePerLine nodeTimeToXY lineto stroke} for
} def

% intervalSecs length ticks -
/ticks
{
    gsave
    currentlinewidth .3 mul setlinewidth
    0 -0.5 translate
    0  2 index  timePerLine
    {0 exch nodeTimeToXY moveto dup 0 exch rlineto stroke} for
    pop
    grestore
} def

% intervalSecs width timelines -
/timelines
{
    gsave
    currentlinewidth mul setlinewidth
    0.7 setgray
    0  exch  timePerLine {
        dup
        0 exch nodeTimeToXY moveto
        totalNodes 1 sub exch nodeTimeToXY lineto
        stroke
    } for
    grestore
} def

% sendNode sendTime recvNode recvTime packet -
/packet
{
    nodeTimeToXY moveto nodeTimeToXY lineto stroke
} def

% lineNo label startLine -
/startLine
{
    exch /lineNo exch def

    totalNodes 1 sub 0 nodeTimeToXY 0.2 add moveto
    show

    0 0 nodeTimeToXY
    currentlinewidth 2 div sub exch currentlinewidth 2 div sub exch
    totalNodes 1 sub timePerLine nodeTimeToXY
    2 index sub exch 3 index sub exch
    currentlinewidth 2 div add exch currentlinewidth 2 div add exch
    1 add
    rectclip

    1 1 timelines
    0.5 0.5 timelines
    0.1 0.1 timelines

    nodes
} def

% x y name key -
/key
{
    gsave
    3 1 roll
    exch 5 mul exch -1 mul -0.5 add translate
    0 0 moveto
    0.75 0 rlineto
    0 -0.4 rlineto
    -0.75 0 rlineto
    0 0.4 rlineto
    fill
    0 setgray
    1.2 -0.35 moveto
    show
    grestore
} def

/Arial findfont
0.5 scalefont
setfont
"""

def openOutput():
    fname = os.tmpnam()
    return open(fname, "w")

def closeOutput(f):
    fname = f.name
    f.close()
    os.spawnlp(os.P_NOWAIT, "gv", "gv", fname)

def secToNsec(secs):
    return long(secs*1000000000)

def nsecToSec(nsecs):
    return float(nsecs)/1000000000

def drawPackets(start, end, nodes, packets, secsPerLine = 10):
    macToNum = {}
    for i, n in enumerate(nodes):
        macToNum[n.macAddr] = i
    descs = set()
    for p in packets:
        descs.add(p.desc)
    descs = list(descs)
    descs.sort()
    descColors = {}
    for i, desc in enumerate(descs):
        descColors[desc] = "%g 1 1 sethsbcolor" % (float(i)/len(descs))

    timeScale = 4.0
    secs = nsecToSec(end-start)
    if secs <= secsPerLine:
        lines = 1
        width = secs
    else:
        lines = int(math.ceil(secs/secsPerLine))
        width = secsPerLine
    keyWidth = max(math.floor(width/5),1)
    keyHeight = int(math.ceil(float(len(descs)) / keyWidth))

    f = openOutput()
    print >> f, preamble
    print >> f, "%%%%BoundingBox: 0 0 %d %d" % \
          (20*(timeScale*width+1), 20*(len(nodes)*lines+keyHeight)+10)
    print >> f, packetsPreamble
    print >> f, "/totalNodes %d def" % len(nodes)
    print >> f, "/timeScale %g def" % timeScale
    print >> f, "0.5 %g translate" % (0.5 + keyHeight)

    print >> f, "gsave"
    for i, desc in enumerate(descs):
        print >> f, descColors[desc]
        print >> f, "%d %d (%s) key" % (i % keyWidth, i / keyWidth, desc)
    print >> f, "grestore"

    def outPacket(srcN, sendNsec, qNsec, dstN, dstNsec, desc):
        print >> f, descColors[desc]
        print >> f, "%d %g %d.2 %g packet" % \
              (srcN, nsecToSec(sendNsec-lineStartNS),
               srcN, nsecToSec(qNsec-lineStartNS))
        print >> f, "%d.2 %g %d %g packet" % \
              (srcN, nsecToSec(qNsec-lineStartNS),
               dstN, nsecToSec(dstNsec-lineStartNS))

    for line in range(lines):
        print >> f, "gsave"
        lineStartNS = start + secToNsec(line * secsPerLine)
        lineEndNS = min(lineStartNS + secToNsec(secsPerLine), end)
        print >> f, "/timePerLine %g def" % nsecToSec(lineEndNS-lineStartNS)
        print >> f, "%d (%g s) startLine" % ((lines - line - 1),
                                             nsecToSec(lineStartNS))
        for p in packets:
            if p.receiveTime < lineStartNS or p.sendTime > lineEndNS:
                continue
            if p.src in macToNum and p.dst in macToNum:
                srcN = macToNum[p.src]
                dstN = macToNum[p.dst]
                outPacket(srcN, p.sendTime, p.queueTime,
                          dstN, p.receiveTime,
                          p.desc)
            elif p.dst.isMulticast():
                for i, dstNode in enumerate(nodes):
                    if i != macToNum[p.src]:
                        outPacket(macToNum[p.src], p.sendTime, p.sendTime,
                                  i, p.receiveTime, p.desc)
            else:
                # XXX Should probably do something smarter than drop
                print "Warning: Unknown packet transit (%s -> %s)" % \
                      (p.src, p.dst)
                pass
        print >> f, "grestore"

    closeOutput(f)
