"""macgen_bin - Generate application from shared libraries"""

import os
import sys
import string
import types
import macfs
from MACFS import *
import MacOS
import Res
import py_resource
import cfmfile
import buildtools


def generate(input, output, module_dict=None, architecture='fat', debug=0):
	# try to remove old file
	try:
		os.remove(output)
	except:
		pass
	
	if module_dict is None:
		import macmodulefinder
		print "Searching for modules..."
		module_dict, missing = macmodulefinder.process(input, [], [], 1)
		if missing:
			import EasyDialogs
			missing.sort()
			answer = EasyDialogs.AskYesNoCancel("Some modules could not be found; continue anyway?\n(%s)" 
					% string.join(missing, ", "))
			if answer <> 1:
				sys.exit(0)
	
	applettemplatepath = buildtools.findtemplate()
	corepath = findpythoncore()
	
	dynamicmodules, dynamicfiles, extraresfiles = findfragments(module_dict, architecture)
	
	print 'Adding "__main__"'
	buildtools.process(applettemplatepath, input, output, 0)
	
	outputref = Res.FSpOpenResFile(output, 3)
	try:
		Res.UseResFile(outputref)
		
		print "Adding Python modules"
		addpythonmodules(module_dict)
		
		print "Adding PythonCore resources"
		copyres(corepath, outputref, ['cfrg', 'Popt', 'GU\267I'], 1)
		
		print "Adding resources from shared libraries"
		for ppcpath, cfm68kpath in extraresfiles:
			if os.path.exists(ppcpath):
				copyres(ppcpath, outputref, ['cfrg'], 1)
			elif os.path.exists(cfm68kpath):
				copyres(cfm68kpath, outputref, ['cfrg'], 1)
		
		print "Fixing sys.path prefs"
		Res.UseResFile(outputref)
		try:
			res = Res.Get1Resource('STR#', 228) # from PythonCore
		except Res.Error: pass
		else:
			res.RemoveResource()
		# setting pref file name to empty string
		res = Res.Get1NamedResource('STR ', "PythonPreferenceFileName")
		res.data = Pstring("")
		res.ChangedResource()
		syspathpref = "$(APPLICATION)"
		res = Res.Resource("\000\001" + Pstring(syspathpref))
		res.AddResource("STR#", 229, "sys.path preference")
		
		print "Creating 'PYD ' resources"
		for modname, (ppcfrag, cfm68kfrag) in dynamicmodules.items():
			res = Res.Resource(Pstring(ppcfrag) + Pstring(cfm68kfrag))
			id = 0
			while id < 128:
				id = Res.Unique1ID('PYD ')
			res.AddResource('PYD ', id, modname)
	finally:
		Res.CloseResFile(outputref)
	print "Merging code fragments"
	cfmfile.mergecfmfiles([applettemplatepath, corepath] + dynamicfiles.keys(), 
			output, architecture)
	
	print "done!"


def findfragments(module_dict, architecture):
	dynamicmodules = {}
	dynamicfiles = {}
	extraresfiles = []
	for name, module in module_dict.items():
		if module.gettype() <> 'dynamic':
			continue
		path = resolvealiasfile(module.__file__)
		dir, filename = os.path.split(path)
##		ppcfile, cfm68kfile = makefilenames(filename)
		ppcfile = filename
		cfm68kfile = "dummy.cfm68k.slb"
		
		# ppc stuff
		ppcpath = os.path.join(dir, ppcfile)
		if architecture <> 'm68k':
			ppcfrag, dynamicfiles = getfragname(ppcpath, dynamicfiles)
		else:
			ppcfrag = "_no_fragment_"
		
		# 68k stuff
		cfm68kpath = os.path.join(dir, cfm68kfile)
		if architecture <> 'pwpc':
			cfm68kfrag, dynamicfiles = getfragname(cfm68kpath, dynamicfiles)
		else:
			cfm68kfrag = "_no_fragment_"
		
		dynamicmodules[name] = ppcfrag, cfm68kfrag
		if (ppcpath, cfm68kpath) not in extraresfiles:
			extraresfiles.append((ppcpath, cfm68kpath))
	return dynamicmodules, dynamicfiles, extraresfiles


def getfragname(path, dynamicfiles):
	if not dynamicfiles.has_key(path):
		if os.path.exists(path):
			lib = cfmfile.CfrgResource(path)
			fragname = lib.fragments[0].name
		else:
			print "shared lib not found:", path
			fragname = "_no_fragment_"
		dynamicfiles[path] = fragname
	else:
		fragname = dynamicfiles[path]
	return fragname, dynamicfiles


def addpythonmodules(module_dict):
	# XXX should really use macgen_rsrc.generate(), this does the same, but skips __main__
	items = module_dict.items()
	items.sort()
	for name, module in items:
		mtype = module.gettype()
		if mtype not in ['module', 'package'] or name == "__main__":
			continue
		location = module.__file__
		
		if location[-4:] == '.pyc':
			# Attempt corresponding .py
			location = location[:-1]
		if location[-3:] != '.py':
			print '*** skipping', location
			continue
		
		print 'Adding module "%s"' % name
		id, name = py_resource.frompyfile(location, name, preload=0, 
				ispackage=mtype=='package')

def Pstring(str):
	if len(str) > 255:
		raise TypeError, "Str255 must be at most 255 chars long"
	return chr(len(str)) + str

##def makefilenames(name):
##	lname = string.lower(name)
##	pos = string.find(lname, ".ppc.")
##	if pos > 0:
##		return name, name[:pos] + '.CFM68K.' + name[pos+5:]
##	pos = string.find(lname, ".cfm68k.")
##	if pos > 0:
##		return name[:pos] + '.ppc.' + name[pos+8:], name
##	raise ValueError, "can't make ppc/cfm68k filenames"

def copyres(input, output, *args, **kwargs):
	openedin = openedout = 0
	if type(input) == types.StringType:
		input = Res.FSpOpenResFile(input, 1)
		openedin = 1
	if type(output) == types.StringType:
		output = Res.FSpOpenResFile(output, 3)
		openedout = 1
	try:
		apply(buildtools.copyres, (input, output) + args, kwargs)
	finally:
		if openedin:
			Res.CloseResFile(input)
		if openedout:
			Res.CloseResFile(output)

def findpythoncore():
	"""find the PythonCore shared library, possibly asking the user if we can't find it"""
	
	vRefNum, dirID = macfs.FindFolder(kOnSystemDisk, kSharedLibrariesFolderType, 0)
	extpath = macfs.FSSpec((vRefNum, dirID, "")).as_pathname()
	version = string.split(sys.version)[0]
	if MacOS.runtimemodel == 'carbon':
		corename = "PythonCoreCarbon " + version
	elif MacOS.runtimemodel == 'ppc':
		corename = "PythonCore " + version
	else:
		raise "Unknown MacOS.runtimemodel", MacOS.runtimemodel
	corepath = os.path.join(extpath, corename)
	if not os.path.exists(corepath):
		fss, ok = macfs.PromptGetFile("Please locate PythonCore:", "shlb")
		if not ok:
			raise KeyboardInterrupt, "cancelled"
		corepath = fss.as_pathname()
	return resolvealiasfile(corepath)

def resolvealiasfile(path):
	try:
		fss, dummy1, dummy2 = macfs.ResolveAliasFile(path)
	except macfs.error:
		pass
	else:
		path = fss.as_pathname()
	return path
