#!/usr/bin/python

import sys,string,os,glob

# *******************************************************************
# *************** lseq V 2.20 - Andrew Whitehurst *******************
# ************************* 16 April 2010 ***************************
# *******************************************************************

# lseq IS A UTILITY THAT SCANS A DIRECTORY AND RETURNS FORMATTED LISTS OF
# SEQUENCES OF FRAMES MAKING IT EASIER TO SEE WHICH FRAMES ARE MISSING
# WHAT THE FRAME RANGES ARE AND SO ON.  IT RETURNS THE LIST IN A "SHAKE-STYLE"
# FORMATTING

# 2.20 FIXES A BUG WHEN SEQUENCES CROSS FROM TENS TO HUNDREDS, HUNDREDS TO THOUSANDS ETC. FROM BEING LISTED AS TWO SEQUENCES

# 2.10 NOW ADDS A FLAG TO HANDLE DIFFERENT NEGATIVE FRAME NUMBER PADDING SCHEMES

# DEFINE ALL THE FUNCTIONS FOR THINGS WE'RE GOING TO NEED THE UTILITY TO DO

# ************ FUNCTIONS BEGIN HERE **************

# BUILD A STRING OF FRAME NUMBERS FROM A SUPPLIED LIST

def makeFrameList(inputList,pad):
	outputString = ""
	if len(inputList) > 1:
		lastTestedNumber = inputList[0]
		minRun = lastTestedNumber # <- minRun WILL BE USED TO NOTE THE LOW NUMBER IN A RUN OF CONSECUTIVE FRAMES
		del inputList[0] # <- WE KNOW THAT WE WILL ALWAYS WANT TO PRINT THE FIRST NUMBER IN THE SEQUENCE AND AS IT'S STORED IN lastNumberTested WE CAN DELETE IT NOW SO IT DOESN'T GET COUNTED TWICE
		inputList.append(inputList[(len(inputList)-1)]+10) # <- I APPEND A VALUE TO THE LIST AS THE LOOP WILL OTHERWISE MISS OFF THE LAST CORRECT VALUE
		maxRun = minRun # <- maxRun WILL MARK THE HIGH VALUE IN A CONSECUTIVE RUN OF FRAMES
		for x in inputList: # <- WORK OUR WAY THROUGH THE LIST OF FRAME NUMBERS
			if x == (lastTestedNumber + 1): # <- TEST FOR A CONSECUTIVE FRAME
				maxRun = x
				lastTestedNumber = x
			elif x != (lastTestedNumber + 1): # <- THE FRAMES AREN'T CONSECUTIVE, TIME TO DECIDE WHAT TO DO
				if maxRun == minRun: # <- LOOKS LIKE A SINGLE FRAME ALL ON ITS OWN
					if maxRun < 0:
						outputString += string.zfill(maxRun,(pad + 1)) + ","# <-- JUST ADD ONE VALUE WITH CORRECT PADDING FOR NEGATIVE NUMBERS
					else:
						outputString += string.zfill(maxRun,pad) + ","# <-- JUST ADD ONE VALUE
					minRun = x
					maxRun = x				
				elif maxRun != minRun and minRun < maxRun: # <- CHECK FOR A LEGITIMATE RUN OF FRAMES
					outputString += (str(minRun) + "-" + str(maxRun) + ",") # <-- ADD A RUN OF VALUES
					minRun = x	
					maxRun = x
				lastTestedNumber = x
		outputString = outputString[:-1] # <-- TAKE THE TRAILING COMMA OFF THE END OF THE SEQUENCE
	elif len(inputList) == 1: # <- IF THE SEQUENCE IS ONLY ONE FRAME THEN JUST OUTPUT IT CHECKING FOR PADDING OF NEGATIVE NUMBERS
		if inputList[0] < 0:
			outputString = string.zfill(inputList[0],(pad + negPad))
		else:
			outputString = string.zfill(inputList[0],pad)

	return outputString

# WHAT TO WRITE IF SOMETHING GOES WRONG

def errorMessage():
	print "\n\tUSAGE : lseq [OPTION] [DIRECTORY]\n"
	print "\tlseq will list numbered sequences in an easy to ready format"
	print "\tthat will show ranges of existing files.  This enables the user"
	print "\tto easily see where files are missing and to see multiple image"
	print "\tsequences contained within the same directory. The list is"
	print "\tin a \"Shake-style\" format, i.e. file.1-7,0010,0015,18-20#.ext\n"
	print "\tlseq will default to treating negative frames like abc.-001.ext as"
	print "\ta four frame padded number. If you wish to treat such a frame as "
	print "\tthree frame padding add the -oldPad flag option.\n"
	print "\tAn optional valid directory path may be specified to run the"
	print "\tthe command on.\n\n"
	sys.exit(1)

# STRING TO INT FUNCTION

def stringToInt(inputString):
	tweakVal = inputString
	outVal = 0
	if inputString[:1] == "-": # <- TAKE CARE OF NEGATIVE VALUES
		multVal = -1
		tweakVal = inputString[1:]
	else:
		multVal = 1
	if tweakVal.isdigit() != 1:
		print(str(tweakVal) + " is a non-numeric frame value.")
	else:
		outVal = int(tweakVal)*multVal
	return outVal


# WORK OUT THE FILENAME STEM

	
def getFileName(inputString):
	outString = " "
	noExt = inputString.rsplit(".",1) # <- CHOP OFF THE FILE EXTENSION
	for i in range(len(noExt[0])):
		if inputString[(len(noExt[0])-i)].isdigit() == 1 and inputString[(len(noExt[0])-(i+1))].isdigit() == 0: # <- FIND WHERE THE NUMBERS STOP GOING RIGHT TO LEFT ALONG THE FILENAME AND FRAME NUMBER
			outString = inputString[0:len(noExt[0])-i]
			break # <- STOP WHEN WE FIND THE END OF THE FRAME NUMBER DIGITS
	if outString[-1:] == "-":
		outString = outString[:-1] # <- REMOVE THE MINUS SIGN FROM NEGAIVE FRAME NUMBERS
	return outString
	
# CALCULATE FRAME NUMBERS
	
def getFrameNumber(inputString):
	outString = "NG"
	noExt = inputString.rsplit(".",1)	
	for i in range(len(noExt[0])):
		if noExt[0][(len(noExt[0])-1-i)].isdigit() == 0 and noExt[0][(len(noExt[0])-1-i)] != "-": # <- WORK RIGHT TO LEFT THROUGH THE STRING TAKING THE NEGATIVE FRAM NUMBER POSSIBILITY INTO ACCOUNT
			outString = noExt[0][len(noExt[0])-i:len(noExt[0])]
			break		
	return outString

# WORK OUT THE FILE'S EXTENSION

def getExtension(inputString):
	outString = ""
	ext = inputString.rsplit(".",1)
	outString = "."+ext[1]
	return outString

# FUNCTION TO RETURN FRAME-PADDING LENGTH - USES THE PREVIOUSLY DEFINED negPad VALUE TO DETERMINE WHTHER WE
# REGARD -#### TO BE 5 FRAME OR 4 FRAME PADDED

def getFramePadding(inputFrame):
	outPad = 0
	if len(inputFrame) > 1 and inputFrame[0:] == "-" and inputFrame[1:] == "0":
		outPad = len(inputFrame)- negPad
	if len(inputFrame) > 1 and inputFrame[0:] == "0":
		outPad = len(inputFrame)
	return outPad





# ******** END OF FUNCTIONS *********

# negPad IS TO HANDLE WHETHER WE REGARD -#### AS FOUR OR FIVE FRAME PADDING

negPad = 0

# MAIN BODY OF CODE STARTS HERE

# DO THE ERROR CHECK THING ON USER INPUT




if len(sys.argv) > 3:
	errorMessage()

if len(sys.argv) >= 2:
	if sys.argv[1] == "-h" or sys.argv[1] == "-help" or sys.argv[1] == "-H" or sys.argv[1] == "--help":
		errorMessage()
	if sys.argv[1] == "-oldPad":
		negPad = 1	



# BUILD THE PATH THAT IS TO BE TESTED, WILL USE THE CWD IF NO DIRECTORY IS SUPPLIED

testPath = ""

if len(sys.argv) >= 2 and sys.argv[1] != "-oldPad":
	if os.path.exists(sys.argv[len(sys.argv)-1]):
		testPath = sys.argv[len(sys.argv)-1]
	else:
		errorMessage()


# BUILD A LIST OF ALL THE RELEVENT FILES

fileList = glob.glob(testPath+"*.*")

# ERROR CHECK FOR THE EXISTENCE OF SOME VALID FILES

if len(fileList) < 1:
	print "\n\tNo valid files found.\n"
	sys.exit(1)

# DEFINE OUR ARRAYS FOR STORING DATA

fileName = []
framePad = []
extension = []
frameNum = []

for x in fileList: # <- BUILD THE LISTS OF ALL THE CHOPPED UP FILE INFO.
	if getFrameNumber(x) != "": # <- FILTER OUT NON-FRAME TYPE FILES
		fileName.append(getFileName(x))
		frameNum.append(getFrameNumber(x))
		framePad.append(getFramePadding(getFrameNumber(x)))
		extension.append(getExtension(x))

uniqueFileName = []
uniqueFramePad = []
uniqueExtension = []
patternExists = 0



# WORK OUT WHICH COMBINATIONS OF FILENAME, FRAME-PADDING AND EXTENSION ARE UNIQUE

if len(fileName) == len(frameNum) and len(fileName) == len(extension):
	for i in range(len(fileName)):
		for x in range(len(uniqueFileName)):
			if fileName[i] == uniqueFileName[x] and framePad[i] == uniqueFramePad[x] and extension[i] == uniqueExtension[x]:
				patternExists = 1
			
		if patternExists != 1:
			uniqueFileName.append(fileName[i])
			uniqueFramePad.append(framePad[i])
			uniqueExtension.append(extension[i])
		else:
			patternExists = 0
else:
	errorMessage()


outputString = "\n" # <- THE OUTPUT STRING TO BE PRINTED TO THE SHELL
tempFrameList = []

for i in range(len(uniqueFileName)): # <- FOR EVERY UNIQUE FILENAME,PAD,EXTENSION COMBO...
	for x in range(len(fileName)): # <- FOR EVERY FILE IN OUR ORIGINAL FULL-LENGTH LIST...
		if fileName[x] == uniqueFileName[i] and framePad[x] == uniqueFramePad[i] and extension[x] == uniqueExtension[i]: # <- IF IT MATCHES THE TEST CONDITION
			if frameNum[x] != "":
				tempFrameList.append(stringToInt(frameNum[x])) # <- ADD THE FRAME TO THE LIST OF FRAMES FOR THIS FILENAME,PAD,EXT COMBO
	tempFrameList.sort()
	if len(tempFrameList) > 1: # <- FOR LISTS LEN >1 BUILD A STRING AND ADD IT TO THE OUTPUT STRING
		outputString += "\t" + str(uniqueFileName[i] + makeFrameList(tempFrameList,uniqueFramePad[i]) + "#" + uniqueExtension[i] + "\n")
	elif len(tempFrameList) == 1: # <- FOR LISTS LEN == 1 BUILD A STRING AND ADD IT TO THE OUTPUT STRING
		outputString += "\t" + str(uniqueFileName[i] + makeFrameList(tempFrameList,uniqueFramePad[i]) + uniqueExtension[i] + "\n")
	tempFrameList[:] = [] # <- CLEAR THE LIST OF FRAMES READY FOR THE NEXT PASS
			
print outputString # <- THIS IS THE FINAL OUTPUT TO THE SHELL

	
