#! /usr/bin/python
"""
#+
# NAME:
#	www_help_dep
# PURPOSE: 
#	Provides dependence tree information for
#	software in the SMEI tree
# CALLING SEQUENCE:
#	www_help_dep <module> -db=<db.txt> -dest=<dest> -show
# INPUTS:
#	module		name of program, function or procedure
#					(default: ipsdt)
#	db.txt		name of data base file containing the
#					dependency information.
#					(default: for.txt)
#					(this is created as part of the
#					www_help system).
#	<dest>		name of an empty, existing directory
#					if specified, this is where copies
#					of all files are written.
#					If omitted information is written
#					to screen only.
#	-show		displays a list of file names on the screen
#	-html		displays html file
# LIMITATIONS:
#	Works for Fortran and IDL code only
# PROCEDURE:
# MODIFICATION HISTORY:
#       SEP-2007, Paul Hick (UCSD/CASS)
#-
"""
import sys, os, cgi

csay = 'www_help_dep'

# ==========
# Reads list of top directories
# Entries in tops have the form $NAME=full_directory

def read_toplist(file):
	tops = (open('www_help.lst', 'r')).read().split()
 
	for top in tops:
		name = top.split('=')
		if file.find(name[0]) == 0:
			break
	else:
		name = file.split('/')
		print 'Do not know where ',name[0],' is'
		return ''

	name = name[1]+file[len(name[0]):]

	return name

def www_help_blank_module():

	# Only the following entries are processed for each module

	blank = dict( {
		'file'   : '' ,
		'calls'  : '' ,
		'include': '' ,
		'external': ''
				} )

	return blank

def www_help_get_db(db_file):

	# Read file with dependence info. Drop trailing empty element after split()

	db_table = (open(db_file, 'r')).read().split('\n')[0:-1]

	db = dict()

	i = 0
	n = len(db_table)

	ndb = 0

	# The data base contains groups of elements
	# module: AdjustJDCar
	# file: $SMEI/for/lib/adjustjdcar.f
	# include: sun
	# external: EARTH
	# see also: Array_Info
	# calls: EARTH,FLINT8,Julian,MAP_TZERO,N_CARRINGTON

	# The "module" and "file" elements are always present (name of
	# function/subroutine/procedure, and the file that contains it)
	# The others are not always present.

	while i < n:

		# Split at semi-colon

		key, line = db_table[i].split(':')
		line = line.strip()

		if key == 'module':	# New module found

			# Add completed module to db

			if ndb > 0:
				db[module] = module_dict

			# Start a new entry

			ndb += 1
			module = line
			module_dict = www_help_blank_module()

		else:				# Process module elements

			if module_dict.has_key(key):

				if line != '(none)':

					# Add another element to the module

					if module_dict[key] == '':
						module_dict[key] = line
					else:
						module_dict[key] += ','+line


		# Next line in db_table

		i += 1

	# Add the last module

	if ndb > 0:
		db[module] = module_dict

	return db

if __name__ == '__main__':

	form = cgi.FieldStorage()

	if form.has_key('html'):

		sys.stderr = sys.stdout

		print 'Content-type: text/html\n'

		html = True
		top_module = form['html'].value
		db_file    = form['db'  ].value

		db_top = read_toplist('$DB')
		db_file = os.path.join( db_top, db_file[len('$DB')+1:] )

	else:
		import tiny

		args = tiny.args(sys.argv)

		if len(args) == 1:
			top_module = 'ipsdt'
		else:
			top_module = args[1]

		show = tiny.is_there('-show',sys.argv)
		html = tiny.is_there('-html',sys.argv)

		db_file = tiny.start('-db=',sys.argv)
		if db_file == '':
			db_file = os.path.join(os.environ['HOME'],'public_html','help','smei','for.txt')

		if not os.path.exists(db_file):
			tiny.say(csay, 'E', db_file, 'file does not exist')
			sys.exit(1)

		destination = tiny.start('-dest=',sys.argv)
		if destination != '':
			if not os.path.isdir(destination):
				tiny.say(csay, 'E', destination, 'directory does not exist')
				sys.exit(1)

			if len(os.listdir(destination)) != 0:
				tiny.say(csay, 'E', destination, 'directory is not empty')
				sys.exit(1)
	

	# Get the list of module with dependency info
	# (calls, external, include)

	db = www_help_get_db(db_file)

	if not db.has_key(top_module):
		if html:
			print 'top module not found: '+top_module
		else:
			tiny.say(csay, 'E',top_module, 'top module not found')

	all_modules = [top_module]
	all_pairs   = []

	i = 0

	while i < len(all_modules):

		module = all_modules[i]			# Next module to be checked

		if db.has_key(module):			# Check if module in db

			# If module in data base append pair 
			# (module name, file name) to all_pairs list

			all_pairs.append( (module,db[module]['file']) )

			# Accumulate all dependencies in add_modules

			add_modules = []

			tmp = db[module]['calls']
			if tmp != '':
				add_modules.extend( tmp.split(',') )

			tmp = db[module]['include']
			if tmp != '':
				add_modules.extend( tmp.split(',') )

			tmp = db[module]['external']
			if tmp != '':
				add_modules.extend( tmp.split(',') )

			# Add new modules to all_modules

			for new_module in add_modules:
				if all_modules.count(new_module) == 0:
					all_modules.append(new_module)

		else:

			all_pairs.append( (module,'<unresolved reference>') )

		i +=1

	# Pick up Fits functions

	fits_archive = '$SMEI/cpp/cfitsio3030/cfitsio3030.tar.gz'

	fits = []						# List of Fits functions
	i = 0
	while i < len(all_pairs):
		pair = all_pairs[i]
		if (os.path.split(pair[1]))[1] == 'forfits.f':
			fits.append(pair[0])	# Name of Fits function
			del all_pairs[i]
		else:
			i += 1

	# Pick up include files. The file names are accumulated in
	# the incl list.

	incl = []
	i = 0
	while i < len(all_pairs):
		pair = all_pairs[i]
		if (os.path.splitext(pair[1]))[1] == '.h':
			incl.append(pair[1])
			del all_pairs[i]
		else:
			i += 1

	# Pick up unresolved references.
	# BListFnc and BReadFnc don't really exist.

	anon = []
	i = 0
	while i < len(all_pairs):
		pair = all_pairs[i]
		if pair[1] == '<unresolved reference>':
			if pair[0] != 'BListFnc' and pair[0] != 'BReadFnc':
				anon.append(pair[0])
			del all_pairs[i]
		else:
			i += 1

	# Accumulate all the file names in all_pairs
	# (note that include files are not present anymore)

	all_files = []
	for pair in all_pairs:
		if all_files.count(pair[1]) == 0:
			all_files.append(pair[1])

	# If forfits.f is on the list then apparently Fits
	# routines are being used.
	# Replace with the Fits tar archive.

	if len(fits) > 0:
		all_files.append(fits_archive)

	# Remove logmod.f (logmod_linux.f should be on the
	# list already.

	this = '$SMEI/ucsd/gen/for/os/logmod.f'
	if all_files.count(this) != 0:
		all_files.remove(this)

	if html:

		print	'<html>\n'      		\
		        '<head>\n'      		\
		        '<title>www_help dependencies, version 2.6</title>\n'  \
		        '</head>\n\n'   		\
		        '<body>\n\n'			\
				'<center>\n\n'

		print	'<table border=1>\n'	\
				'<tr>'

		print  '  <td><b>%s</b> uses %d modules from %d files</td>' \
				%(top_module,len(all_modules),len(all_files))

		print	'</tr>\n'		\
				'</table>\n'	\
				'<br />\n\n'

		print	'<table border=1>'

		for pair in all_pairs:
			print '<tr>'
			print '  <td>%s</td>'%pair[0]
			print '  <td>%s</td>'%pair[1]
			print '</tr>'

		print	'</table>\n\n'

		if len(incl) > 0:

			print	'<br />\n'			\
					'<table border=1>\n'\
					'<tr>'

			print  '  <td><b>%s</b> depends on %d include files</td>'% \
					(top_module,len(incl))

			print	'</tr>\n'		\
					'</table>\n\n'

			print	'<br />\n'		\
					'<table border=1>'

			for this in incl:
				print '<tr>'
				print '  <td>%s</td>'%this
				print '</tr>'

			print	'</table>\n\n'


		if len(anon) > 0:

			print	'<br />\n'				\
					'<table border=1>\n'	\
					'<tr>'

			print  '  <td><b>%s</b> has %d unresolved reference</td>'% \
					(top_module,len(anon))

			print	'</tr>\n'		\
					'</table>\n\n'

			print	'<br />\n'		\
					'<table border=1>'

			for this in anon:
				print '<tr>'
				print '  <td>%s</td>'%this
				print '</tr>'

			print	'</table>\n\n'


		if len(fits) > 0:

			print	'<br />\n'		\
					'<table border=1>\n'	\
					'<tr>'

			print  '  <td><b>%s</b> depends on %d Fits routines from %s</td>'% \
					(top_module,len(fits),fits_archive)

			print	'</tr>\n'		\
					'</table>\n\n'

			print	'<br />\n'		\
					'<table border=1>'

			for this in fits:
				print '<tr>'
				print '  <td>%s</td>'%this
				print '</tr>'

			print	'</table>\n\n'


		print	'</center>\n\n'	\
				'</body>\n'		\
				'</html>\n'

	elif destination != '':

		# Add include files to all_files list

		for f in incl:
			all_files.append(f)

		near = []
		for f in all_files:
			near_file = os.path.expandvars(f)
			far_file  = os.path.join(destination,os.path.basename(near_file))
			sts = os.spawnlp(os.P_WAIT, 'cp','cp','-vpf', near_file, far_file)
			near.append(near_file)

		near_file = os.path.join(destination,'files.txt')
		near = '\n'.join(near)+'\n'
		open(near_file,'w').write(near)

		anon_file = os.path.join(destination,'anon.txt')
		anon = '\n'.join(anon)+'\n'
		open(anon_file,'w').write(anon)

		tiny.say(csay,'I','total', '%d modules in %d files'%(len(all_modules),len(all_files)))

	elif show:

		for pair in all_pairs:
			print '%-30s %s'%pair

		for this in incl:
			print '%-30s %s'%('',this)

		for this in anon:
			tiny.say(csay,'W','module not found,', this)	

		tiny.say(csay,'I','total', '%d modules in %d files'%(len(all_modules),len(all_files)))

	else:

		for this in anon:
			tiny.say(csay,'W','module not found,', this)	

		tiny.say(csay,'I','total', '%d modules in %d files'%(len(all_modules),len(all_files)))

	sys.exit(0)
