#! /usr/bin/env python

# Require imports
import sys
import os
import shutil
import tempfile
#global variables
rotationArray = []
movingAtoms = []
numrotablegroups = 0
edititem=''
inpfilename=''
headerfile=''
datafile=''
tailfile=''
dataArray=[0]
inputdirectory=''
outputdirectory=''
crepespath = sys.path[0]

def usage():
	print "Usage: crepes []"
	print "				  gamess input file"
	print "				  input files must end in inp"
def inpfiletest(argv):
	global inpfile
	global inpfilename
	if len(sys.argv) == 1:
		inpfilename = raw_input('Enter a GAMESS input file: ')
	else:
		inpfilename = sys.argv[1]
	if  inpfilename.endswith("inp"):
		return
	else:
		print "This is not a games input file."
		sys.exit()
def fillrotationArray(edititem):
	global rotationArray
	#test if this is a new rotation or and edit of a misentered
	if edititem != 0:
		rotation = edititem - 1
		fillrotationquest(rotation)
	else:
		for rotation in range(numrotablegroups):
			fillrotationquest(rotation)
def fillrotationquest(rotation):
			global edititem
			print "----"
# 	Debug Prints			
#			print "edititem = " + str(edititem)
#			print "rotation = " + str(rotation)

# Expand array for new rotations
			if edititem == 0:
				rotationArray.extend([0])
			rotationArray[rotation] = [0 , 0, 0, 0, 0, 0]
# Fill the rotation array			
			question = "Enter atom number of rotation point 1  for rotation " + str(rotation+1) + ": "
			inputcheck = 0
			while inputcheck==0:
				try:
					rotationArray[rotation][0] =  int(raw_input(question))
					inputcheck = 1
				except:
					print "This is not an integer"
			question = "Enter atom number of rotation point 2  for rotation " + str(rotation+1) + ": "
			inputcheck = 0
			while inputcheck==0:
				try:
					rotationArray[rotation][1] =  int(raw_input(question))
					inputcheck = 1
				except:
					print "This is not an integer"
			question = "Number of atoms moving by rotation " + str(rotation+1) + ": "
			inputcheck = 0
			while inputcheck==0:
				try:
					rotationArray[rotation][2] =  int(raw_input(question))
					inputcheck = 1
				except:
					print "This is not an integer"
			stepcheck = 0
			while stepcheck == 0:
				print 'For rotations less than 360 add one step size to total rotation'
				question = "Total rotation (in degrees) " + str(rotation+1) + " (360) : "
				inputcheck = 0
				stepsize =  raw_input(question)
				if stepsize == "":
					rotationArray[rotation][3] = 360
				else:
					while inputcheck==0:
						try:
							rotationArray[rotation][3] =  int(stepsize)
							inputcheck = 1
						except:
							print "This is not an Number"
				question = "Step size (in degrees) of rotation " + str(rotation+1) + " (120) : "
				inputcheck = 0
				stepsize =  raw_input(question)
				if stepsize == "":
					rotationArray[rotation][4] = 120
				else:
					while inputcheck==0:
						try:
							rotationArray[rotation][4] =  int(stepsize)
							inputcheck = 1
						except:
							print "This is not an Number"
# Check Input for logic error
				if rotationArray[rotation][4] < rotationArray[rotation][3]:
					stepcheck = 1
				else:
					print 'Rotation step is larger than the total rotation.'
				

# Tag for compound rotations
# Originaly intended to make non compound rotations more efficient by storing rather than 
# calculating each step. In practice found that calculation was not a signifcant impediment
# therfore setting to denote all as compound in spite of there being no functionality implemented
#			question = "Is rotation " + str(rotation+1) + " a compound rotation (Y/n): "
#			inputcheck = 0
#			while inputcheck==0:
#				compound = raw_input(question)
#				try:
#					rotationArray[rotation][5] =  bool(eval(compound))
#					inputcheck = 1
#				except:
#					if compound == "":
#						rotationArray[rotation][5] = bool(1)
#						inputcheck = 1
#					elif  compound == "y":
#						rotationArray[rotation][5] = bool(1)
#						inputcheck = 1
#					elif compound == "n":
#						rotationArray[rotation][5] = bool(0)
#						inputcheck = 1
#					else:
#						print "This is not a Boolean"
				rotationArray[rotation][5] = bool(1)
def fillmovingatom(edititem):
# Check if this is a new rotation or a edit of misentered data
	if edititem != 0:
		rotation = edititem - 1
		fillmovingquest(rotation)
	else:
		for rotation in range(numrotablegroups):
			fillmovingquest(rotation)
def fillmovingquest(rotation):
# fill the moving atoms array
	print "----"
	global movingAtoms
	movingAtoms.extend([0])
	movingAtoms[rotation]=[]
	for atom in range(rotationArray[rotation][2]):
		movingAtoms[rotation].extend([0])
	question = "Enter the atoms that move for rotation " + str(rotation+1) + " (comma separated): "
	inputcheck = 0
	while inputcheck==0:
# test to see if if the number of atoms moving matches how many the entered
		try:
			rotablelist = list(eval("(" + raw_input(question) + ",)"))
			if  len(rotablelist) == rotationArray[rotation][2]:
				try:
					movingAtoms[rotation] =  rotablelist
					movingAtoms[rotation].sort()
					inputcheck = 1
				except:
					print "This is not a list"
			else:
				confimmovables = raw_input('You entered ' +  str(rotationArray[rotation][2]) + ' for the number of atoms that need to be moved for this rotation;\n You entered a different number of atoms.\n Is this list correct? (y/n) ')
				if confimmovables == 'y':
					movingAtoms[rotation] = rotablelist 
					rotationArray[rotation][2] = len(movingAtoms[rotation])
					inputcheck = 1
		except:
			print "This is not a list"
def inputquestions():
#initiate data acquisition edititem=0 indicates a new rotation
	inputcheck=0
	global numrotablegroups
	global edititem
	while inputcheck == 0:
		try:
			numrotablegroups = int(raw_input('Please enter the number of rotable groups: '))
			inputcheck = 1
		except:
			print "This is not an integer"
	edititem=0
	fillrotationArray(edititem)
	fillmovingatom(edititem)
def confirminput():
# Print the inputed data for review before executing 
	for rotation in range(numrotablegroups):
		print '---'
		print 'Rotation ' + str(rotation+1)
		print 'Axix of rotation: ' + str(rotationArray[rotation][0]) + " , " + str(rotationArray[rotation][1])
		print 'Number of atoms moving with rotation: ' + str(rotationArray[rotation][2])
		print 'Angle of rotation total/steps: ' + str(rotationArray[rotation][3]) + " / " + str(rotationArray[rotation][4])
		print 'Compound rotation: ' + str(rotationArray[rotation][5])
		print 'Atoms that move for rotation: ' + str(movingAtoms[rotation])
def editinput():
# after displaying data allow user to indicate changes that need to be made
	print "---"
	confirmed = raw_input( "Are all of these correct (Y/n): ")
	if confirmed == '':
		confirmed = 'y'
	if confirmed != 'y':
		question='Enter the rotation that needs to be corrected [1-' +str(numrotablegroups) + ']: '
		inputcheck = 0
		while inputcheck==0:
			global edititem
			edititem = raw_input(question)
			try:
				edititem = int(edititem)
				inputcheck=1
			except:
				print "This is not an integer"
		print "Correcting " + str(edititem)
		fillrotationArray(edititem)
		fillmovingatom(edititem)
		confirminput()
		editinput()
def parseinpfile():
# Parse input deck so that the file is also broken up so that it can be reassembles after rotations
	pathisclear = 0
	while pathisclear == 0:
		global inpfilename
		global inpfile
		print 'Reading ' + inpfilename
		inpfile = open(inpfilename)
		global headerfile
		global datafile
		global tailfile
		global inputdirectory
		inputdirectory = inpfilename.rstrip('.inp') + '.input/'
		if not os.path.exists(inputdirectory):
			os.mkdir(inputdirectory)
			os.chdir(inputdirectory)
			headerfile = open('headerfile.txt', 'w')
			datafile = open('datafile.txt','w')
			tailfile = open('tailfile.txt','w')
			sym = 0
			for line in inpfile:
				if sym == 0:
					headerfile.write(line)
				if 'C1' in line:
					sym=1
				if '$END' in line and sym == 1:
					sym =  2
				if sym == 1 and 'C1' not in line:
					datafile.write(line)
				if sym == 2:
					tailfile.write(line)
			print "Parse complete. "
			headerfile.close()
			datafile.close()
			tailfile.close()
			os.chdir('..')
			pathisclear = 1
		else:
			print "Directory " + inputdirectory + " already exits."
			overwrite = raw_input("Overwrite(Y/n):")
			if overwrite == 'y':
				shutil.rmtree(inputdirectory,ignore_errors=True)
			elif overwrite=="":
				shutil.rmtree(inputdirectory,ignore_errors=True)
			else:
				sys.exit()
def copyInputFile():
# Prepare a place for generation of output files and copy data over 
	global inpfilename
	global inputdirectory
	global outputdirectory
	pathisclear = 0 
	while pathisclear == 0:
		global inpfilename
		global outputdirectory
		outputdirectory = inpfilename.rstrip('.inp') + '.output/'
# Check if path already exists and query user for what to do
		if not os.path.exists(outputdirectory):
# 	Debug Prints
#			print os.getcwd()
			os.mkdir(outputdirectory)
			instr= inputdirectory + 'datafile.txt'
			outstr = outputdirectory + 'datafile.txt'
			shutil.copy( instr, outputdirectory)
			pathisclear = 1
		else:
			print "Directory " + outputdirectory + " already exits."
			overwrite = raw_input("Overwrite(Y/n):")
			if overwrite == 'y':
				shutil.rmtree(outputdirectory,ignore_errors=True)
			elif overwrite=="":
				shutil.rmtree(outputdirectory,ignore_errors=True)
			else:
				sys.exit()
	return 
def createdataArray():
# create an array of the incoming data
	global dataArray
	datafile = open(inputdirectory + 'datafile.txt','r')
	for line in datafile:
		dataArray.extend([line.rstrip("\n")])
	datafile.close()
def formateddatafile():
# format input data so that it can properly be handled by C++ code
	os.chdir(outputdirectory)
	formateddatafile = open('formateddatafile.txt','w')
	datafile = open('datafile.txt','r')
	for line in datafile:
# 	Debug Prints
#		print line.rstrip('\n')
		splitline = line.split()
		for element in range(len(splitline)):
			splitline[element] = splitline[element] + ","
		string = ''.join(splitline)
		string = string.replace('.0,','.0\t')
		string = string.rstrip(",")
# 	Debug Prints
#		print string
		formateddatafile.write(string+'\n')
	formateddatafile.close()
	os.chdir("..")
def iterator( rotationParameters ):
# Itterate through all possible combinations of rotations and generate files
	totalIndexLength = len(rotationParameters.rotationArray)
	# open the current file  and load contents into memory for use later
	currentdataArray = []
	currentDataFile = open(rotationParameters.currentFileNameStr,'r')
	for line in currentDataFile:
		currentdataArray.extend([line.rstrip("\n")])
	currentDataFile.close()
	# perform my rotations 
	# set up some variables for driving the for loop based off our first input data element
# 	Debug Prints
#	print "rotationParameters.currentIndex " + str(rotationParameters.currentIndex)
	if rotationParameters.currentIndex >= totalIndexLength:
		return 
	firstPropertiesElement = rotationParameters.rotationArray[rotationParameters.currentIndex]
# 	Debug Prints
#	print 'Testing syntax'
#	print firstPropertiesElement
#	print rotationArray
#	print movingAtoms
	startingDegree = 0
	endingDegree = firstPropertiesElement[3]
	stepDegree = firstPropertiesElement[4]
# 	Debug Prints
#	print "startingDegree " + str(startingDegree)
#	print "stepDegree " + str(stepDegree)
#	print "endingDegree " + str(endingDegree)
	# loop through for my for loop
	degreeCounter = startingDegree
	while degreeCounter < endingDegree:
		# what information do we need to rotate; pass it into the rotateAtoms call
		localRotationParameters = rotationParameters.clone()
		localRotationParameters.currentDegree = degreeCounter
		rotationResultdataArray = rotateAtoms( currentdataArray,  localRotationParameters ) 
# 	Debug Prints
#		print "rotationResultdataArray", rotationResultdataArray
#		for line in rotationResultdataArray:
#			print line
		# get a new file name
		oldFileNameStr = rotationParameters.currentFileNameStr.rstrip('.txt')
		newFileNameStr = oldFileNameStr + "." + str(degreeCounter) + ".txt"
		# loop through the rotationresult and write it out to the new file
		# save the rotations  to a new data file based off the rotations:  "datafile.0.txt"	
#		print currentdataArray
		print "Writing " + str(newFileNameStr)
		newDataFile = open( newFileNameStr, 'w' )
# 	Debug Prints
#		print "currentdataArray "
#		linecounter=0
#		for line in currentdataArray:
#			linecounter=linecounter+1
#			print linecounter,line
#		print "rotationResultdataArray "
#		for line in rotationResultdataArray:
#			print line
#		print "rotationParameters.currentIndex ", rotationParameters.currentIndex
		rotatedatomcounter=2
		for atom in movingAtoms[rotationParameters.currentIndex]:
			if rotatedatomcounter < len(rotationResultdataArray):
# 	Debug Prints
#				print "atom ", atom
#				print "rotatedatomcounter ", rotatedatomcounter
#				print "rotationResultdataArray[rotatedatomcounter] ", rotationResultdataArray[rotatedatomcounter]
				currentdataArray[atom-1]=rotationResultdataArray[rotatedatomcounter]
				rotatedatomcounter = rotatedatomcounter + 1
			else:
				break
		for line in range(len(currentdataArray)):
			currentdataArray[line] = currentdataArray[line].replace('       ',',',2)
			currentdataArray[line] = currentdataArray[line].replace('      ',',')
			currentdataArray[line] = currentdataArray[line].replace(',','      ',1)
			currentdataArray[line] = currentdataArray[line].replace('.0,','.0\t',1)
# 	Debug Prints
#		print "currentdataArray after replace "
#		linecounter=0
#		for line in currentdataArray:
#			linecounter=linecounter+1
#			print linecounter,line
		for atom in currentdataArray:
			# write to the file
			newDataFile.write(atom + "\n")
		newDataFile.close()

		# check to see if we should recurse; stopping condition
# 	Debug Prints
#		print "totalIndexLength " + str(totalIndexLength)
#		print "rotationParameters.currentIndex " + str(rotationParameters.currentIndex)
		if rotationParameters.currentIndex < totalIndexLength:
			newRotationParameters = rotationParameters.clone()
			newRotationParameters.currentIndex = rotationParameters.currentIndex +1
			newRotationParameters.currentDegree = degreeCounter
			newRotationParameters.currentFileNameStr = newFileNameStr
			iterator( newRotationParameters )
		#increment degrees based on propertries of input
		degreeCounter = degreeCounter + stepDegree
def rotateAtoms( dataArray, rotationParameters):
	returndataArray = dataArray
	# do the rotation of the atoms, whatever that is, need to identify what information we need to do the rotation and call the c execution
	# takes the input dataArray and copies it/modifies it and fills the return Data Array so it can be written to disk
	#index	
	rotateIndex = rotationParameters.currentIndex
	#angle
	rotateCurrentDegree = rotationParameters.currentDegree
	#rotationArray values
	rotateArrayValues = rotationParameters.rotationArray[rotateIndex]
	#moving atoms
	rotateMovingAtoms = rotationParameters.movingAtoms[rotateIndex]
	#step size
	rotatestepsize= rotationParameters.rotationArray[rotateIndex][4]
# 	Debug Prints
#	print rotateArrayValues[0]
#	print rotateArrayValues[1]
#	print dataArray[rotateArrayValues[0]-1]
#	print dataArray[rotateArrayValues[1]-1]
#	print dataArray
#	print currentdataArray
#	print range(len(dataArray))
	sourceData = tempfile.NamedTemporaryFile()
	sourceData.write(dataArray[rotateArrayValues[0]-1]+"\n")
	sourceData.write(dataArray[rotateArrayValues[1]-1]+"\n")
	for line in range(len(dataArray)):
		if line + 1 in rotateMovingAtoms:
#			print dataArray[line]
			sourceData.write(dataArray[line]+"\n")
	sourceData.seek(0)
# 	Debug Prints
#	print ""
#	print "tempfile"
#	print sourceData.read()
#	sourceData.seek(0)
	#atoms to rotate around
	# look up in atoms data based on ids in moving attoms and rotation array
	returndataArray = rotateAtom(rotateMovingAtoms,  rotatestepsize, rotateCurrentDegree, sourceData.name)
# 	Debug Prints
#	print"returned data"
#	for atom in returndataArray:
#		print atom.rstrip("\n")
	return returndataArray
def rotateAtom(rotateMovingAtoms,  rotatestepsize, rotateCurrentDegree, sourceData):
# Call the rotate atom C++ code
	returnRotation = []
#	sourceData = 'formateddatafile.txt'
# 	Debug Prints
#	print 'str(len(rotateMovingAtoms))' + str(len(rotateMovingAtoms))
#	print 'str(rotateCurrentDegree)' + str(rotateCurrentDegree)
#	print "str(sourceData)" + str(sourceData)
	# the rotator imports previous step coordinates therefore it gets passed the step size 
	if rotateCurrentDegree == 0:
#		print "executing ","./crot " + str(len(rotateMovingAtoms)) + " " + str(rotateCurrentDegree) + " " + str(sourceData)
		tempstore = os.popen(str(crepespath)+"/crot " + str(len(rotateMovingAtoms)) + " " + str(rotateCurrentDegree) + " " + str(sourceData), 'r')
	else:
#		print "executing ","./crot " + str(len(rotateMovingAtoms)) + " " + str(rotatestepsize) + " " + str(sourceData)
		tempstore = os.popen(str(crepespath)+"/crot " + str(len(rotateMovingAtoms)) + " " + str(rotatestepsize) + " " + str(sourceData), 'r')
	for line in tempstore:
		returnRotation.append(line.rstrip('\n'))
#	for line in returnRotation:
#		print line.rstrip('\n')
	return returnRotation
class RotationParameters:
# define a class object that passes information up and down the itteration
	def __init__ (self, currentIndex = 0, rotationArray = None, movingAtoms = None, currentFileNameStr = None, currentDegree = None ):
		self.currentIndex = currentIndex
		self.rotationArray = rotationArray
		self.movingAtoms = movingAtoms
		self.currentFileNameStr = currentFileNameStr
		self.currentDegree = currentDegree
	def clone( self):
		returnParameters = RotationParameters()
		returnParameters.currentIndex = self.currentIndex
		returnParameters.rotationArray = self.rotationArray
		returnParameters.movingAtoms = self.movingAtoms
		returnParameters.currentFileNameStr = self.currentFileNameStr
		returnParameters.currentDegree = self.currentDegree
		return returnParameters
def fileformat():
# format the files that are output from the C++ code
# recompile the input deck from the headers of the original file
	basename = inpfilename.rstrip('inp')
	for file in os.listdir(outputdirectory):
		syntaxname = file.lstrip("formateddatafile.")
		dataname = syntaxname.rstrip('txt')
		filename = basename + dataname + 'inp'
		shutil.copy(inputdirectory + 'headerfile.txt' , outputdirectory + filename)
		print "Formating ", filename
		openfile = open(outputdirectory + filename, 'a')
		opendatafile = open(outputdirectory + file,'r')
		for line in opendatafile:
			formline = line
			formline = formline.replace('\t',',')
			formline = formline.replace(',',',  ')
			formline = formline.replace('  -',' -')
			formline =  formline.replace(',','\t')
			openfile.write(formline)
		tailfile = open(inputdirectory + 'tailfile.txt', 'r')
		for line in tailfile:
			openfile.write(line)
def cleanup():
#clean out unnecessary files from output that will cause problems in the format step
	try:
		os.remove(outputdirectory + 'datafile.txt')
	except:
		print 'File datafile.txt does not exist'
	try:
		os.remove(outputdirectory + 'formateddatafile.txt')
	except:
		print 'File formateddatafile.txt does not exist'
	maxseps=0
	for file in os.listdir(outputdirectory):
		seps = file.count('.')
		if seps > maxseps:
			maxseps = seps
	for file in os.listdir(outputdirectory):
		seps = file.count('.')
		if seps < maxseps:
# 	Debug Prints
#			print file,seps,maxseps
			os.remove(outputdirectory+file)
def finalcleanup():
#Clear files that are not used by the user may be helpful in testing to not call this function
	print "Cleaning up"
	for file in os.listdir(outputdirectory):
		if "txt" in file:
			try:
				os.remove(outputdirectory + file)
			except:
				print "File " + file + "Does not exist"
	shutil.rmtree(inputdirectory)
def main(argv):
# Main thread of the crepes function calls teh individual steps
	print '\n CREPES \n'
	inpfiletest(argv)
	inputquestions()
	confirminput()
	editinput()
	parseinpfile()
	copyInputFile()
	createdataArray()
	formateddatafile()
	# setup the parameters for the rotations
	newBaseFile = outputdirectory + "formateddatafile.txt"
	myRotationParameters = RotationParameters()
	myRotationParameters.currentIndex = 0
	myRotationParameters.rotationArray = rotationArray
	myRotationParameters.movingAtoms = movingAtoms
	myRotationParameters.currentFileNameStr = newBaseFile
	# run the rotations
	iterator( myRotationParameters )
	cleanup()
	fileformat()
# 	Debug Prints
#	print rotationArray
#	print movingAtoms
	finalcleanup()

main(sys.argv)
