#!/usr/bin/python
'''Make a semicircle plot of the references within a tex file'''

# Tex Reference Graph (TRG) by Emily Dumas, v 0.0.2
# This source file is in the public domain

import sys
import getopt

def usage():
        print('''
    usage: trg2 [options] [INFILE]

    Read tex source from INFILE or STDIN and create a graph of internal references.

    Options:
      -h or --help
          Display this help message.
      -p or --ps or --postscript
          Select postscript semicircle graph output (default)
      -t or --text
          Select text output, i.e. a list of references
      -o OUTFILE or --output OUTFILE
          Send output to OUTFILE (default: STDOUT)
      -v or --verbose
          Be verbose.  Use twice for extra verbosity.
''')   

try:
    opts, args = getopt.gnu_getopt(sys.argv[1:], 'hpvto:',
                                   ['ps', 'postscript', 'text',
                                    'output=','verbose'])
except getopt.GetoptError:
    usage()
    sys.exit(2)

mode = 'postscript'
verbose = False
outfn = ''
stdout = True

for o, a in opts:
    if o in ('-h', '--help'):
        usage()
        sys.exit() 
    if o in ('-p','--ps','--postscript'):
        mode = 'postscript'
    if o in ('-t','--text'):
        mode = 'text'
    if o in ('-o','--output'):
        outfn = a
        stdout = False
    if o in ('-v','--verbose'):
        if not verbose:
            verbose = True
        else:
            verbose = 'very'

if args:
    infile = file(args[0])
    if len(args) > 1:
        sys.stderr.write('Warning: ignoring extra non-option arguments \"%s\"\n' % args[1:])
else:
    infile = sys.stdin

                     
import re
RE_START = re.compile(r'\\begin{document}')
#RE_SEC = re.compile(r'\\(?:sub)*section{([^}]+)}')
RE_REF = re.compile(r'\\ref{([^}]+)}')
RE_LABEL = re.compile(r'\\label{([^}]+)}')

# Find the start of the document
npre = 0
for line in infile:
    npre = npre + 1
    if RE_START.match(line):
        break

labeldict = {}
refdict = {}
n = 1
for line in infile:
    m = RE_LABEL.search(line)
    if m:
        labeldict[m.group(1)] = n
        if verbose == 'very':
            sys.stderr.write('Found label "%s"\n' % m.group(1))
    m = RE_REF.search(line)
    if m:
        refdict[n] = m.group(1)
        if verbose == 'very':
            sys.stderr.write('Found reference to "%s"\n' % m.group(1))
    n = n + 1

if verbose:
    sys.stderr.write('Processed %d lines of source, %d labels, %d references.\n' % (npre+n,len(refdict),len(labeldict)))

klist = refdict.keys()
klist.sort()

# DONE PROCESSING, open the output file
if stdout:
    outfile = sys.stdout
else:
    outfile = file(outfn,'wt')

if mode == 'text':
    # TEXT OUTPUT
    for k in klist:
        if refdict[k] in labeldict:
            if verbose == 'very':
                sys.stderr.write('%s -> %s\n' % (k,labeldict[refdict[k]]))
            outfile.write('%s\t%s\n' % (k,labeldict[refdict[k]]))
    else:
        if verbose:
            sys.stderr.write('BROKEN reference at line %s\n' % k)
    outfile.close()
    sys.exit()


# POSTSCRIPT GRAPHICS OUTPUT
pshdr='''%%!PS-Adobe-2.0 EPSF-2.0
%%%%BoundingBox: %d %d %d %d
%%%%Title: PS Graphics
%%%%EndComments
/m {newpath moveto} bind def
/l {lineto} bind def
/cp {closepath} bind def
/s {stroke} bind def
/sg {setgray} bind def
/a {arc stroke} bind def
0.25 setlinewidth
1 setlinecap
1 setlinejoin
'''

psftr='showpage\n'

margin = 20.0

def makess(ctrx,rad,ctry=margin,arrow=0.1):
    s = '%.2f %.2f %.2f 0 180 a\n' % (ctrx,ctry,rad)
    if arrow != None:
        s += '%.2f %.2f m\n' % (ctrx + arrow * rad, ctry + rad * (1.0 - 0.6 *abs(arrow)))
        s += '%.2f %.2f l\n' % (ctrx, ctry + rad)
        s += '%.2f %.2f l\ns\n' % (ctrx + arrow * rad, ctry + rad * (1.0 + 0.6*abs(arrow)))
    return s

refpairs = [ (k,labeldict[refdict[k]]) for k in klist if (refdict[k] in labeldict) and (labeldict[refdict[k]] < k) ]
maxjump = max( [ x-y for (x,y) in refpairs ] )

bbw = 595.0
bbh = 2.0*margin + (0.55 * maxjump / float(n) * bbw)
factor = (bbw - 2.0*margin) / n

outfile.write(pshdr % (0,0,int(bbw),int(bbh)))
for x,y in refpairs:
    outfile.write(makess(margin + factor * 0.5*(x + y), factor*0.5*(x-y)))
outfile.write(psftr)
