#!/usr/bin/env python

"""
	The program "MMA - Musical Midi Accompaniment" and the associated
	modules distributed with it are protected by copyright.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	Bob van der Poel <bvdp@uniserve.com>
	
"""

import sys
import os

# Ensure a proper version is available.

pyMaj=2
pyMin=3

try:
	if sys.version_info[0] < pyMaj or sys.version_info[1] < pyMin:
		raise
except:
	print
	print "You need a more current version of Python to run MMA."
	print "We're looking for something equal or greater than version %s.%s" % (pyMaj,pyMin)
	print "Current Python version is ", sys.version
	print
	sys.exit(0)

""" MMA uses a number of application specific modules. These can
	be placed in a number of locations. Some people like application
	specific modules to be in an application directory....for these
	people mma appends the moddirs[] list to the current sys.path[].
	Otherwise, the modules should be in the site-packages directory
	of the main python tree. The imports are surrounded by a try/expect.
	If any of the imports fail, the error message is printed (note, we
	can't use the normal error() routine here since it's in a module that
	we can't find.
"""

moddirs=["/usr/local/share/mma/modules", "/usr/share/mma/modules", "./modules"] 
sys.path.extend(moddirs)

""" Try to load the nesc. modules for mma. Catch ONLY ImportErrors.
	This means that if there is an error in the found module, that
	error will dump out of python and let python report; however,
	if we can't find the modules, then the expect will print our
	"you installed wrong" message.
"""

for a in (
	"import MMAglobals ; gbl = MMAglobals",
	"from MMAcommon import *",
	"import MMAoptions",
	"import MMAmidi",
	"import MMAdocs",
	"from MMAparse import parseFile, setMidiFileType",
	"from MMAfile import locFile",
	"from MMAlyric import lyric" ):

		try:
			exec(a)
	
		except ImportError:
			print "MMA was unable to find the module directory (or a"
			print "specific module). The module directory contains Python"
			print "extensions necessary for operation. In addition to the"
			print "standard site-packages tree, mma tried the additional"
			print "directories:"
			print "  ", moddirs
			print
			print "Please review the installation."
			print "Error occured with module: %s" % a.split()[1]
			print "Contact Bob if you can't figure it out!"

			sys.exit(1)



########################################
########################################

# Start of Program

###########################
# Get our command line stuff

MMAoptions.opts()

# LibPath and IncPath are set in MMAglobals. Debug setting isn't set
# when the default is done.

if gbl.debug:
	print "Initialization has set LibPath set to", gbl.libPath			
	print "Initialization has set IncPath set to", gbl.incPath			


#######################################
# Set up initial meta track stuff. Track 0 == meta

m = gbl.mtrks[0]=MMAmidi.Mtrk(0)

m.addText(0, "Created by MMA") 
m.addTrkName(0, 'MetaTrack')
m.addTempo(0, gbl.tempo) 
MMAmidi.timeSig.set(4, 2)

#####################################
# Read an RC file. All found files are processed.

# Disable doc printing for RC file

docOption = gbl.docs
gbl.docs = 0

rcread=0
for i in ('/etc/mmarc', '/usr/local/etc/mmarc',
		os.path.expanduser("~/.mmarc"),	'mmarc' ):

	f = locFile(i, None)
	if f:
		if gbl.showrun:
			print "Reading RC file '%s'" % f
		parseFile(f)
		rcread+=1

if not rcread:
	gbl.lineno = -1
	warning("No RC file was found or processed")


gbl.docs = docOption



################################################
# Update the library database file(s) (-g option)
# Note: This needs to be here, after reading of RC files

if gbl.makeGrvDefs:
	if gbl.infile:
		error("No filename is permitted with the -g option")
	from MMAauto import libUpdate
	libUpdate(gbl.makeGrvDefs)		# update and EXIT
	

################################
# We do need an input file for anything after this point.
	
if not gbl.infile:
	MMAoptions.usage("No input filename specified.")

################################
# Just extract docs (-Dx) to stdout.

if docOption:
	f=locFile(gbl.infile, None)
	if not f:
		error("File '%s' not found." % gbl.infile)
	parseFile(f)
	sys.exit(0)

	
# These cmdline options override settings in RC files

if gbl.cmdSMF:
	gbl.lineno = -1
	setMidiFileType(['SMF=%s' % gbl.cmdSMF])
	
# Add filename to meta track.

gbl.mtrks[0].addText(0, "Input filename: %s" % gbl.infile)

##########################################
# Create the output filename.
# If outfile was specified on cmd line then leave it alone.
#	Otherwise ... 
#	1. strip off the extension if it is .mma,
#	2. append .mid 
#   3. if gbl.outpath is set:
#      insert outpath in front of filename

if gbl.outfile:
	outfile = "%s/%s" % (gbl.outPath, gbl.outfile)
else:
	outfile, ext = os.path.splitext(gbl.infile)
	if ext != gbl.ext:
		outfile=gbl.infile
	outfile += '.mid'
	
	if gbl.outPath:
		if gbl.outPath[0] in '.\\/':
			outfile = "%s/%s" % (gbl.outPath, outfile)
		else:
			head, tail = os.path.split(outfile)
			outfile = "%s/%s/%s" % (head, gbl.outPath, tail)
		
outfile=os.path.expanduser(outfile)

################################################
# Read/process files....

# First the mmastart files

for f in gbl.mmaStart:
	fn = locFile(f, gbl.incPath)
	if not fn:
		warning("MmaStart file '%s' not found/processed." % fn)
	parseFile(fn)
	gbl.lineno = -1

# The song file specified on the command line

f = locFile(gbl.infile, None)
if not f:
	error("Input file '%s' not found." % gbl.infile)	
parseFile(f)


# Finally, the mmaend files

for f in gbl.mmaEnd:
	fn = locFile(f, None)
	if not fn:
		warning("MmaEnd file '%s' not found/processed." % f)
	parseFile(fn)


#################################################
# Just display the channel assignments (-c) and exit... 

if gbl.chshow:
	print
	print "MMA tracks allocated:"
	k=gbl.tnames.keys()
	k.sort()
	max=0
	for a in k + gbl.deletedTracks:
		if len(a)>max:
			max = len(a)
	max+=1
	wrap=0
	for a in k:
		wrap += max
		if wrap>60:
			wrap = max
			print 
		print " %-*s" %( max, a),
	print
	print
	if gbl.deletedTracks:
		print "Deleted Tracks:"
		wrap=0
		for a in gbl.deletedTracks:
			wrap += max
			if wrap>60:
				wrap=max
				print
			print " %-*s" %( max,a),
		print
		print
	print "MMA channel assignments:"
	ch=gbl.midiAssigns.items()
	ch.sort()
	for c, n in ch:
		if n:
			wrap = 3
			print " %2s" % c,
			for nn in n:
				wrap += max
				if wrap>63:
					print "\n   ",
					wrap=max+3
				print "%-*s" % (max,nn),

			print
	print "\nFile '%s' parsed, but no MIDI file produced!" % gbl.infile
	print
	sys.exit(0)


####################################
# Dry run, no output

if gbl.noOutput:
	warning( "Input file parsed successfully. No midi file generated.")
	sys.exit(0)

##############################
# Create the output (MIDI) file

gbl.lineno=-1	# disable line nums for error/warning
fileExist = os.path.exists(outfile)

""" Check if any pending midi events are still around. Mostly
	this will be a DRUM event which was assigned to the 'DRUM'
	track, but no DRUM track was used, just DRUM-xx tracks used.
"""

for n in gbl.tnames.values():
	if n.channel:
		n.doMidiClear()
		n.clearPending()
		if n.riff:
			warning("%s has pending Riff(s)" % n.name)

""" Check all the tracks and find total number used. When
	initializing each track (class) we made an initial entry
	in the track at offset 0 for the track name, etc. So, if the
	track only has one entry we can safely skip the entire track.
"""

keys=gbl.mtrks.keys()
keys.sort()
trackCount=1	# account for meta track

for n in keys[1:]:	 # check all but 0 (meta)
	if len(gbl.mtrks[n].miditrk) > 1:
		trackCount += 1

if trackCount == 1:	# only meta track
	if fileExist:
		print
	print "No data created. Did you remember to set a groove/sequence?"
	if fileExist:
		print "Existing file '%s' has not been modified." % outfile
	sys.exit(1)

lyric.leftovers()

if fileExist:
	print "Overwriting existing",
else:
	print "Creating new",
print "midi file '%s'" %  outfile

try:
	out = file(outfile, 'wb')
except:
	error("Can't open file '%s' for writing." % outfile)

MMAmidi.writeTracks(out)
out.close()

if gbl.debug:
	print "Completed processing file '%s'." % outfile






