#!/usr/local/bin/python
"""Join a ColdC textdump from a lot of .cdc files.

Neale Pickett <zephyr@roguetrader.com>

The trick here is figuring out what has to go before what else."""

import glob, string, regsub, regex, sys, getopt

# How many percentage points until a new # is drawn?
checkpoint = 2


# An error we might throw
dependency = "dependency"

# Some globals
checkpoints = []
f = 0					# This will be a file later.
done = []
children = {}
depends = {}
textdump = 'textdump'
tree = 0
noisy = 1
index = 'src/+INDEX'
version = '1.0'

def write_bar(s):
   """Write out a status bar."""

   if noisy:
      sys.stdout.write('%s: [%s]\r%s: [' % (s,
					    '-' * (100/checkpoint),
					    s))
      sys.stdout.flush()



def dependencies():
   """Calculate dependencies."""

   func_def = regex.compile('^\(new *\)?object \([^ :]*\)\( *: *\(.*\)\)?;$')
   
   for obj in files:
      if obj in checkpoints and noisy:
	 sys.stdout.write('#')
	 sys.stdout.flush()
   
      f = open('src/%s.cdc' % obj)
      while 1:
	 line = f.readline()
	 if not line:
	    break
	 loc = func_def.match(line)
	 if loc != -1:
	    try:
	       deps = func_def.group(4)
	       if not deps:
		  break
	       deps = map(lambda o: o[1:], regsub.split(deps, ', *'))
	       for i in deps:
		  try:
		     children[i].append(obj)
		  except KeyError:
		     children[i] = [obj]
		  try:
		     depends[obj].append(i)
		  except KeyError:
		     depends[obj] = [i]

	    except 'foo':
	       pass


def descend(space, node, last=0):
   """Write to the file and build a nice tree in the process.

   This is a recursive function, and a gnarly one at that."""
   
   # Make sure we've already printed all the dependencies
   try:
      deps = depends[node]
      for i in deps:
	 if i not in done:
	    raise dependency, (node, i)
   except KeyError:
      deps = ()

   # Put this into the tree
   if len(deps) > 1:
      label = "%s %s" % (node, `deps`)
   else:
      label = node
      
   if last:
      ret = ['%s`--%s' % (space, label)]
      space = space + '   '
   else:
      ret = ['%s|--%s' % (space, label)]
      space = space + '|  '


   # Print to the textdump only if we haven't already
   if node not in done:
      # This is a pretty bogus way to do checkpoints, but at least you
      # get a growing bar.  In my tests, the growth isn't even that jerky.
      if node in checkpoints and noisy:
	 sys.stdout.write('#')
	 sys.stdout.flush()
      src = open('src/%s.cdc' % node)
      f.writelines(src.readlines())
      done.append(node)			# Mark as done


   # now print out all the kids
   try:
      kids = children[node]
   except KeyError:
      return ret

   deps = []
   while len(kids) > 0:
      _kids = kids
      kids = []
      for i in _kids:
	 try:
	    if i == _kids[-1] and kids == []:
	       last = 1
	    else:
	       last = 0
	    ret = ret + descend(space, i, last)
	 except dependency, dnode:
	    if dnode[1] in _kids:
	       kids.append(i)
	    else:
	       if dnode[1] in deps:
		  raise dependency, dnode
	       else:
		  deps.append(dnode[1])
		  kids.append(i)

   return ret




#################################################################
##
## Here is where the program starts

# Parse arguments
basename = string.split(sys.argv[0], '/')[-1]
try:
   optlist, args = getopt.getopt(sys.argv[1:], 'qhtvi:o:')
except getopt.error, str:
   print '%s: %s' % (basename, str)
   print "Try `%s -h' for more information." % basename
   sys.exit(0)
for pair in optlist:
   c = pair[0][1]
   if c == 'q':
      noisy = 0
   elif c == 't':
      tree = 1
   elif c == 'i':
      index = pair[1]
   elif c == 'o':
      textdump = pair[1]
   elif c == 'v':
      print 'tdjoin version %s' % version
      sys.exit(0)
   elif c == 'h':
      print 'Usage: %s [OPTIONS]' % basename
      print """Merges .cdc files into a textdump

-q          Shut up (don't print status)
-t          Print hierarchy tree
-i [FILE]   Write index to FILE instead of 'src/+INDEX'
-o [FILE]   Write textdump to FILE instead of 'textdump'
-v          Print version and exit
-h          Print help (this) and exit

Neale Pickett <zephyr@roguetrader.com>"""
      sys.exit(0)


# First, glob for all the files we need to play with
if noisy:
   sys.stdout.write('Building list of files...')
   sys.stdout.flush()
files = map(lambda fn: fn[4:-4], glob.glob("src/*.cdc"))
if noisy:
   print ' done.'


# Set up for the status bar
# That's right, damnit, we're going to do this thing the *right* way!
num = len(files)
granularity = (checkpoint/100.0) * num
for i in range((100.0/checkpoint)):
   checkpoints.append(files[num - int(round(granularity * i)) - 1])


# Calculate dependencies (build the dicts)
write_bar('Calculating dependencies')
dependencies()
if noisy:
   print


# Write out the textdump and build the tree
write_bar('    Ordering and writing')


# I wait until here to do this so that the existing textdump will
# survive if we don't make it this far
f = open(textdump, 'w')

if 0:
   print
   for i,j in depends.items():
      print '%s: %s' % (i, j)

tr = descend('', 'root', 1)

# We made it out, so print out the index.  It just so happens that the
# items in done are already in the right order for this file.
f = open(index, 'w')
for i in done:
   if i:
      f.write(i + '\n')
f.close()

if noisy:
   print
if tree:
   for i in tr:
      print i