# _-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_
#	Copyright (c) 2019 the ACME Software Deli. All rights reserved.
#
#	Name:	plmPreProc
#
#	Description:
#		This is a rather simple-minded pre-processor for PL/M-86 source code which
#		translates the __FILE__ and __LINE__ pre-processor directives introduced in
#		the C language into a PL/M-86 equivalent.  This is EXTREMELY helpful for
#		debugging and logging
#
#	Revision History:
#	x001	rmg		Fri Jul 21 13:30:03 2019
#		initial coding
#
#	x002	rmg		Sun Dec 15 13:42:12 2019
#		when writing to the output file, write the original line unless there
#		have been substitutions, then write those instead.  That will help to preserve
#		the indentation
#
#	x003	rmg		Sun Jun 12 10:24:53 2022
#		put the module ID string immediately after the logger.ext include.  This
#		will remove the ID strings for modules where the logger has been
#		conditionally removed
# _-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_

versionString = "x003"
copyrightString = "Copyright (c) 2019, 2022 the ACME Software Deli.\nAll rights reserved"

#	ensure that the local common modules are visible

import __init__

#	common, from Python framework

import os
import sys
import platform
import argparse
import fileinput

#	common, from the local python library

import logger
import fileIO

#	just some variables that more than one piece of the app will need

inputFileName = None
outputFileName = None

# ========================================================================
#	name: changeExtension()
#
#	Description:
#	Using the filename specified, remove the extension and change it to the
#	newly specified extension. for instance, "navBuilder.py", "ini" would
#	translate to "navBuilder.ini"
# ========================================================================

def changeExtension( rootName, newExtension ):
	splitName = os.path.splitext( rootName )

	if ( ( newExtension == None ) or ( newExtension == "" ) ):
		returnName = splitName[ 0 ]
	else:
		returnName = splitName[ 0 ] + '.' + newExtension

	return ( returnName )
#	end changeExtension()

# ========================================================================
#	name: simpleName()
#
#	Description:
#	Using the complex (fully-qualified) filename, return only the basename
#	without the extension.  for instance, "/usr/mgillmore/work/tools/navbuilder.py"
#	as the input, translates to "navbuilder"
# ========================================================================

def simpleName( complexName ):
	filename = os.path.basename( complexName )

	return ( os.path.splitext( filename )[ 0 ] )
#	end simpleName()

# ========================================================================
#	Function:	composeUserGreeting()
#
#	Description:
#		this function queries the OS for its name, version and configuration
#		that information is combined with the version number and name
#		of the application along with the copyright statement
# ========================================================================

def composeUserGreeting():
	uname = platform.uname()
	returnStr = "\n{} {}({}) - {}, Version {}\n{}\n".format(
			str( uname.system ), str( uname.release ), str( uname.version ),
			simpleName( sys.argv[ 0 ] ), versionString,
			copyrightString )

	return ( returnStr )
#	end composeUserGreeting()

# ========================================================================
#	name: addArgDefinitions()
#
#	Description:
#		this method is a helper, defining the command options
# ========================================================================

def addArgDefinitions( parser ):
	#	the argument parser has been created, now add the argument
	#	definitions

	parser.add_argument( '-v', '--verbose',
		help="report additional instrumentation to the log",
		action="store_true", default=False )

	parser.add_argument( '-if', '--inputFile',
		help="the name of the file to be parsed which may contain the macros " + \
			"which are be processed",
		action="store", default=None )

	parser.add_argument( '-of', '--outputFile',
		help="(optional) the name of the file in which the pre-processed output " + \
			"will be stored.  if no file is specified, the input file will be " + \
			"renamed as .ppp, and the output file will contain the name of the " + \
			"input file.",
		action="store", default=None )

#	end addArgDefinitions()

# ========================================================================
#	name: parseCommandArgs()
#
#	Description:
#		this method reads and interprets the command line
# ========================================================================

def parseCommandArgs():
	global inputFileName
	global outputFileName

	returnCode = 0

	#	Create the arg parser, then add the defined parameters

	parser = argparse.ArgumentParser( prefix_chars = '-' )
	addArgDefinitions( parser )

	#	parse the args (defined in sys.argv by default)

	args = parser.parse_args()

	#	since we have parsed the arguments, interpret and store them

	if ( args.verbose ):
		logger.changeLevel( logger.DEBUG_L )

	inputFileName = args.inputFile
	if ( inputFileName == None ):
		#	if an input file has not been specified, we have a failure

		parser.print_help()
		returnCode = 1
	else:
		#	we have a valid input file specified.  the output file will be
		#	managed as necessary

		outputFileName = args.outputFile

	return ( returnCode )
#	end parseCommandArgs()

# ========================================================================
#	Function:	preprocessor()
#
#	Description:
#		read the input file, interpret and write to the output file
# ========================================================================

def preprocessor( nameForLocationString, inputHandle, outputHandle ):
	sourceLineNumber = 1
	moduleIDWritten = False

	for inputLine in inputHandle.readlines():
		logger.addToLog( logLevel = logger.DEBUG_L,
			text = "read '{}'".format( inputLine ) )

		if ( not moduleIDWritten ):
			#	read from the source file, line by line until after the FIRST
			#	"do;" statement.  At that point, write the moduleIDString data
			#	declaration to the file

			lineTokens = inputLine.split()
			logger.addToLog( logLevel = logger.DEBUG_L,
				text = "tokens in line: {}".format( lineTokens ) )

			if ( len( lineTokens ) > 0 ):
				#	look to see if this is the $include (logger.ext) line

				for tokenIndex in range( len( lineTokens ) ):
					logger.addToLog( logLevel = logger.DEBUG_L,
						text = "found '{}'".format( lineTokens[ tokenIndex ].lower() ) )

					if ( 'logger.ext' in lineTokens[ tokenIndex ].lower() ):
						outputHandle.write( inputLine )
						outputHandle.write(
							"declare moduleIDString (*) byte data ( '{}', 0 );\n".format(
								nameForLocationString ) )
						moduleIDWritten = True
				#	end foreach token
				if ( not moduleIDWritten ):
					outputHandle.write( inputLine )
			else:
				outputHandle.write( inputLine )
		#	end ! moduleIDWritten
		else:
			lineTokens = inputLine.split()
			logger.addToLog( logLevel = logger.DEBUG_L,
				text = "tokens in line: {}".format( lineTokens ) )

			if ( len( lineTokens ) > 0 ):
				#	__FILE__ will be substituted with @moduleIDString
				#	__LINE__ will be substituted with the sourceLineNumber

				tokenModified = False
				for tokenIndex in range( len( lineTokens ) ):
					logger.addToLog( logLevel = logger.DEBUG_L,
						text = "token[{}]: {}".format( tokenIndex, lineTokens[ tokenIndex ] ) )
					if ( lineTokens[ tokenIndex ] == '__FILE__,' ):
						lineTokens[ tokenIndex ] = "{},".format( '@moduleIDString' )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == ',__FILE__,' ):
						lineTokens[ tokenIndex ] = ",{},".format( '@moduleIDString' )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == ',__FILE__' ):
						lineTokens[ tokenIndex ] = ",{}".format( '@moduleIDString' )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == '__FILE__' ):
						lineTokens[ tokenIndex ] = "{}".format( '@moduleIDString' )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == '__LINE__,' ):
						lineTokens[ tokenIndex ] = "{},".format( sourceLineNumber )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == ',__LINE__,' ):
						lineTokens[ tokenIndex ] = ",{},".format( sourceLineNumber )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == ',__LINE__' ):
						lineTokens[ tokenIndex ] = ",{}".format( sourceLineNumber )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == '__LINE__' ):
						lineTokens[ tokenIndex ] = "{}".format( sourceLineNumber )
						tokenModified = True
					elif ( lineTokens[ tokenIndex ] == '__LINE__;' ):
						lineTokens[ tokenIndex ] = "{};".format( sourceLineNumber )
						tokenModified = True
				#	end for each token in the line

				if ( not tokenModified ):
					outputHandle.write( inputLine )
				else:
					for token in lineTokens:
						outputHandle.write( "{} ".format( token ) )
					outputHandle.write( "\n" )
			else:
				outputHandle.write( inputLine )
		#	end moduleID already written

		sourceLineNumber += 1
	#	for each line in the input

#	end preprocessor()

# ========================================================================
#	Function:	processFiles()
#
#	Description:
#		create the output file, read the input file, translating each line
#		to the output file  (note:  a locationFileName variable will be
#		created in each file that is pre-processed)
# ========================================================================

def processFiles():
	global inputFileName
	global outputFileName

	#	first thing, create the output file.  if the output file's name is
	#	the same as the input file, rename the input file before creating
	#	the output file

	if ( outputFileName == inputFileName ):
		modifiedInputName = inputFileName + ".ppp"
		fileIO.renameFile( inputFileName, modifiedInputName )
	elif ( outputFileName == None ):
		modifiedInputName = inputFileName + ".ppp"
		fileIO.renameFile( inputFileName, modifiedInputName )
		outputFileName = inputFileName
	else:
		modifiedInputName = inputFileName

	logger.addToLog( logLevel=logger.DEBUG_L,
		text="inputName: '{}' outputName: '{}'".format(
				modifiedInputName, outputFileName ) )

	#	create the output file, and open the input file

	inputFileHandle = open( modifiedInputName, 'r' )
	outputFileHandle = open( outputFileName, 'w' )

	#	do the work we have intended

	preprocessor( modifiedInputName, inputFileHandle, outputFileHandle )

	#	close the files, and name according to the user wishes

	inputFileHandle.close()
	outputFileHandle.close()

#	end processFiles()

# ========================================================================
#	Function:	main()
#
#	Description:
#		the top level function for this application (corresponds to main()
#		in C and C++ applications)
# ========================================================================

def main():
	userGreetingStr = composeUserGreeting()
	print( userGreetingStr, end='' )

	if ( 0 == parseCommandArgs() ):
		processFiles()
#	end main()

# ========================================================================
#	the real program entry point
# ========================================================================

if ( __name__ == "__main__" ):
	main()

