#
#   PSPTools.py
#
#   Utility functions

true = 1
false = 0
MaxLineLength = 85

runInPSP = true

if runInPSP:
	from PSPApp import *

import string
import types

# remove newline chars from a string and return the modified string
def stripNewlines(str):
	return string.replace(str, chr(10), " ")
		
# *******************************************************************************
#   Function:    RemoveComments
#   Author:      Steve Neumeyer
#   Purpose:     remove comment markers from some python code
#   Returns:     the updated command string with comments markers removed
#   Parameters:  text - the string containing the code
#   Notes:       
# ********************************************************************************
def RemoveComments(text):
	# deletelist is a list of char positions that we will delete
	deletelist = []

	blockSize = len(text)

	pos = 0
	# mark the position of all hash marks in the command text for deletion
	while pos < blockSize-1:
		while text[pos] == '#' or text[pos] == ' ' or text[pos] == chr(9):
			if text[pos] == '#':
				deletelist += [pos]
			pos += 1

		while text[pos] != chr(10) and pos < blockSize-1:
			pos += 1
		if text[pos] == chr(10):
			pos += 1

	pos = len(deletelist)-1
	# delete the hash marks from the command text
	while pos >= 0:
		delIndex = deletelist[pos]
		cmdText = text
		text = cmdText[:delIndex] + cmdText[delIndex+1:]
		pos -= 1

	return text

#******************************************************************************
#   Function:    AddComments
#   Author:      Steve Neumeyer
#   Purpose:     comment out some python code
#   Returns:     the updated command string with comments inserted
#   Parameters:  text - the string containing the code to be commented
#   Notes:       
#******************************************************************************
def AddComments(text):

	# insert a comment marker at the beginning
	text = '#' + text

	insertlist = []
	pos = 0

	# if we find a newline char, put its index location into a list
	for x in text:
		if x == chr(10):
			insertlist += [pos]
		pos += 1

	blockLen = len(text)
	stringCtr = blockLen
	posCtr = len(insertlist)-1

	# cycle backwards thru the block, inserting a '#' at all marked locations
	while posCtr>=0:
		text = text[:insertlist[posCtr]+1] + '#' + text[insertlist[posCtr]+1:]
		posCtr -= 1

	return text		
	
def FormatParameterRepository( PR, StartIndentLevel = 4 ):
	''' apply PSP Style formatting to a parameter repository.
		This means K&R style braces on formatting of a dict,
		with each key on its own line.  List and tuple types
		are word wrapped at column 85, though I have not tried to
		ensure that the word wrap algorithms are identical'''
	CurrentIndent = StartIndentLevel
	OutStr = '{\n'
	CurLine = ' ' * CurrentIndent
	FirstVal = 1
	
	# we assume that the parameter coming in is always a dict
	for key in PR.keys():
		
		# on all keys but the first have to put a comma and newline to advance
		if not FirstVal:
			CurLine += ',\n'
			OutStr += CurLine
			CurLine = ' ' * CurrentIndent
			
		FirstVal = 0
		CurLine += "'" + key + "'"
		CurLine += ': '
		if type(PR[key]) == types.DictionaryType:
			CurLine += FormatParameterRepository( PR[key], CurrentIndent + 4 )
		elif type(PR[key]) == types.ListType:
			CurLine += FormatList( PR[key], CurrentIndent, len(CurLine), '[', ']' )
		elif type(PR[key]) == types.TupleType:
			CurLine += FormatList( PR[key], CurrentIndent, len(CurLine), '(', ')' )
		else:
			CurLine += repr(PR[key])

	# put out the last new line and the closing brace
	CurLine += '\n'
	OutStr += CurLine
	CurLine = ' ' * CurrentIndent + '}'
	OutStr += CurLine
	
	return OutStr

def FormatList( ListVal, CurrentIndent, CurLineLength, OpenChar, CloseChar ):
	''' format a list using the same parameters as PSP.
		Values are put out on the same line until we reach out max line
		length, then we start a new line, apply the indent and keep
		going.'''

	OutStr = ''
	CurLine = OpenChar
	FirstVal = 1
	
	for Val in ListVal:
		if not FirstVal:
			CurLine += ', '

		FirstVal = 0
		
		if len(CurLine) + CurLineLength > MaxLineLength:
			OutStr += CurLine
			CurLine = ' ' * CurrentIndent
			CurLineLength = 0
			
		if type(Val) == types.DictionaryType:
			CurLine += FormatParameterRepository( Val, CurrentIndent + 4 )
		elif type(Val) == types.ListType:
			CurLine += FormatList( Val, CurrentIndent, len(CurLine), '[', ']' )
		elif type(Val) == types.TupleType:
			CurLine += FormatList( Val, CurrentIndent, len(CurLine), '(', ')' )
		else:
			CurLine += repr(Val)

	OutStr += CurLine
	OutStr += CloseChar +  ' '
	return OutStr

#******************************************************************************
#   Function:    changeExecutionMode
#   Author:      Steve Neumeyer
#   Purpose:     given a string representing python source for a command,
#					set the execution mode specified.
#   Returns:     the updated command string
#   Parameters:  pyStr - python command as a string
#				 exeMode - the new execution mode to set into the python command
#   Notes:       
#******************************************************************************
def changeExecutionMode(pyStr, newExeMode):
   # newMode = 'App.Constants.ExecutionMode.' + newExeMode
	beginIndex = string.find(pyStr, 'ExecutionMode')
	beginIndex += len('ExecutionMode')
	beginIndex += 3 # skip over the ' : and <space> chars
	endIndex = string.find(pyStr[beginIndex:], ",")
	if endIndex < 0:
		endIndex = string.find(pyStr[beginIndex:], '}')
	pyStr = pyStr[:beginIndex] + 'App.Constants.ExecutionMode.' + newExeMode + pyStr[beginIndex+endIndex:]
	return pyStr

# return the execution mode given the command string as python code
def getExecutionMode(pyStr):
	beginIndex = string.find(pyStr, 'ExecutionMode')
	beginIndex += len('ExecutionMode')
	beginIndex += 2 # skip over the ' and : chars
	endIndex = string.find(pyStr[beginIndex:], ",")
#	crIndex = string.find(pyStr[beginIndex:], chr(10))
#	if crIndex < endIndex and crIndex >= 0:
#		endIndex = crIndex
	# its the last entry in GeneralSettings, so terminate with a '}'
	if endIndex < 0: 
		bracketIndex = string.find(pyStr[beginIndex:], '}')
		newlineIndex = string.find(pyStr[beginIndex:], chr(10))
		if bracketIndex < newlineIndex:
			endIndex = bracketIndex
		else:
			endIndex = newlineIndex
	exeMode = string.strip( pyStr[beginIndex:beginIndex+endIndex] )
	temp = string.replace(exeMode, 'App.Constants.ExecutionMode.', '')
	return temp

# the App.Edit() extension currently will add quotes around constants
# like so: 'App.Constants.Boolean.True' what we actually want is:
# App.Constants.Boolean.True without the single quote marks
def removeQuotesFromPSPConstants(dictAsString):
	
	start = 0
	constantIdx = string.find(dictAsString, 'App.Constants', start)

	# remove trailing quotes
	while constantIdx > -1: # while we have constants

		QuoteIdx = string.find(dictAsString, '\'', constantIdx)
		NewlineIdx = string.find(dictAsString, '\n', constantIdx)
		CommaIdx = string.find(dictAsString, ',', constantIdx)
		
		# if we find a ' before a \n or , then there is a quoted constant...
		if (QuoteIdx < NewlineIdx) or (QuoteIdx < CommaIdx):
			# remove the trailing quote
			dictAsString = dictAsString[:QuoteIdx] + dictAsString[QuoteIdx+1:]
			start = QuoteIdx + 1
		else:
			start = constantIdx + 1
		
		# go find the next constant, if any
		constantIdx = string.find(dictAsString, 'App.Constants', start)
		
	# remove the leading quotes
	dictAsString = string.replace(dictAsString, '\'App.Constants', 'App.Constants')

	return dictAsString	
	

#******************************************************************************
#   Function:    GetQuotedFields
#   Author:      Steve Neumeyer
#   Purpose:     extract quoted strings from the complete text of a command.
#			     store the strings in a list
#   Returns:     a list containing individual script parameters
#   Parameters:  commandtext - the complete text of a script (raw)
#   Notes:       this function will ignore nesting and get everything in quotes
#		ATTENTION: the code assumes that the script generator will only generate one
#			key/value pair per line for each item in a command's dictionary
#******************************************************************************
def GetQuotedFields(commandtext):
	quotepositions = [] # initialize empty list
	result = 0
	
	while (result != -1):
		temp = result
		result = string.find(commandtext, "'", result)
		doubleQuote1 = string.find(commandtext, '"', temp)
		doubleQuote2 = string.find(commandtext, '"', doubleQuote1 + 1)
		escapedQuote = string.find(commandtext, "\\'", temp)
		if escapedQuote != -1:
			escapedQuote += 1

		# get the location of the last quote mark in the current line,
		# this will be used to ensure there is not a false match when
		# detecting an escaped quote as in the following script fragment:
		# 'Folder': 'D:\\',
		lastquoteonline = 0
		if escapedQuote != -1:
			eolposition = string.find(commandtext, '\n', temp)
			lastquoteonline = string.rfind(commandtext, "'", temp, eolposition)		
		
		# add the quote position to the list, but not if its an embedded quote: \'
		if (result != -1):	# if a single quote was found
			if (result != escapedQuote): # if the single quote found was not an escaped quote
				if (result > doubleQuote1) and (result < doubleQuote2):
					result = result + 1  # ignore single quote within double quotes
				else:
					quotepositions = quotepositions + [result] # add index to list
					result = result + 1
			# if this block is executed, the parser thinks it found and embedded quote,
			# but that may not actually be the case.
			elif (result == escapedQuote) and (escapedQuote == lastquoteonline):
				quotepositions = quotepositions + [result] # add index to list
				result = result + 1
			else:
				result = result + 1 # ignore that single quote

	# this error will get printed if there is not an
	# even number of quotes in the command text
	if (len(quotepositions) % 2 != 0) :
		print 'Quote mark error in the command script'

	# now fill up list with command name and attributes
	commandinfo = []
	i = 0
	while ( i<(len(quotepositions)) ) :
		# command component text to the list
		commandinfo = commandinfo + [commandtext[quotepositions[i]+1:quotepositions[i+1]]]
		i = i+2
		
	return commandinfo            
	# test for an even number of quotes here (optional)

#******************************************************************************
#   Function:    GetDictFromCmdString
#   Author:      Steve Neumeyer
#   Purpose:     given a string representing a PSP command, return a dictionary
#				 containing the repository attributes for the command
#   Returns:     a list containing individual script parameters
#   Parameters:  commandtext - the complete text of a script (raw)
#   Notes:       this function will ignore nesting and get everything in quotes
#******************************************************************************
def GetDictFromCmdString(cmdstring):
	index2delete = -1
	begindictindex = string.find(cmdstring, '{')

	#	dictstring = cmdstring[begindictindex:]
	cmdstring = cmdstring[begindictindex:]

	# remove the ')' char at the end of the string
	strlength = len(cmdstring)
	x = strlength-1
	while (x>0):
		if cmdstring[x] == ')':
			index2delete = x
			break
		x -= 1
	if index2delete == -1:
		dictstring = cmdstring
	else:
		dictstring = cmdstring[:index2delete]

	return eval(dictstring)

def GetPropertiesDict(cmdstring):
	index2delete = -1
	begindictindex = string.find(cmdstring, '{')

	cmdstring = cmdstring[begindictindex:]

	dictstring = cmdstring
	return eval(dictstring)
	
