import os
import subprocess
import sys

#----------------------------------------------------
# description of one fusion instance
class CombinationDesc:
	inputCombinationNumber = 0
	threshold = 0
	folder = ''

	def __init__(self,inC,T,path):
		self.inputCombinationNumber = inC
		self.threshold = T
		self.folder = path

	def report(self):
		print("combNo="+str(self.inputCombinationNumber)
		      +", thres="+str(self.threshold)
		      +", folder="+self.folder)

#----------------------------------------------------
# maps: SEGvalue = CombinationDesc
mapOfBest = {}

# CHANGE HERE TO ADJUST HOW MANY BEST VALUES ONE WANTS TO KEEP
keepNBest = 10


def RemoveResult(folder):
	if (os.path.isdir(folder)):
		# remove files inside this folder
		for ff in os.listdir(folder):
			os.remove(folder+os.sep+ff)

		# remove the folder itself
		os.rmdir(folder)
	else:
		print("Strange! Missing folder: "+folder)


def AddNextResult(SEG,combination):
	if SEG in mapOfBest:
		# the same result already exists, replace it with the new one
		RemoveResult(mapOfBest[SEG].folder)

	# add this reult (replaces if mapOfBest[SEG] was already existing)
	mapOfBest[SEG] = combination

	if (len(mapOfBest) > keepNBest):
		worstKey = sorted( mapOfBest.keys() )[0]
		worstComb = mapOfBest[worstKey]

		# remove the first/worst one result from the map
		mapOfBest.pop(worstKey)

		# remove its result files
		RemoveResult(worstComb.folder)


# example on how to operate the class and this function:
# (it records the result if among 'keepNBest' ones;
#  in any case, it maintains folders of only those in 'mapOfBest')
#
# sample call:
# AddNextResult(0.65,CombinationDesc(6,4,"/tmp/C"))
#                |                   | |
#                SEG                 | threshold used
#                                    |
#                                    combination number

#----------------------------------------------------
# the main script starts here:
if(len(sys.argv.__str__().split()) < 8):	# First parameter calls this function
	sys.exit("There should be at least 7 parameters:\n"
	        +"1. Path Fiji binary\n"
	        +"2. Output Folder\n"
	        +"3. Video Name\n"
	        +"4. Time Points list\n"
	        +"5. TRA Folder\n"
	        +"6. N, Number of fixed inputs >= 0\n"
	        +"7. Input Fixed Folder 1\n"
	        +"..."
	        +"7+N Input Fixed Folder N\n"
	        +"7+N+1 Input Folder N+1\n"
	        +"7+N+2 Input Folder N+2\n"
	        +"...")

inputFolders=[]
for i in range(7,len(sys.argv)):
   inputFolders.append(sys.argv[i])

#print(["INPUTS here: ",inputFolders])
# list of input folders with participants' segmentations
#inputFolders = ["C:/testing/01_RES-01",
#                "C:/testing/01_RES-14"]
fijiBinary = sys.argv[1]
outputFolder = sys.argv[2]	# into this folder, all outputs should be generated
videoname = sys.argv[3]
timesStr = sys.argv[4]
gtFolder = sys.argv[5]		# folder with tracking markers

fixedInputsN = int(sys.argv[6])
inputsIncrement = 2**fixedInputsN

N = len(inputFolders)
#N=0 -- 000000000001 == 1 << 0
#N=1 -- 000000000010 == 1 << 1
#N=2 -- 000000000100 == 1 << 2
#N=3 -- 000000001000 == 1 << 3
#N=4 -- 000000010000 == 1 << 4

# the combinations sampling happens here
# the current combination is stored in the array of paths: inFolders
for j in range(inputsIncrement-1, 2**N, inputsIncrement):		# examine combination, bit by bit from the right
	inFolders = []
	for i in range(0,N):
		if (j & (1 << i)) > 0:	# if i-th bit is 1, then add inputFolders[i]
			inFolders.append(inputFolders[i])

	Nfol = len(inFolders)
	# this goes over all possible thresholds given there is N input segmentations
	for T in range(1, Nfol+1):
		# non-colliding name of the output folder + create it
		outFolder = outputFolder + "/merged_" + videoname + "_" + str(j).zfill(5) + "_T=" + str(T)
		if(not os.path.isdir(outFolder)):
			os.makedirs(outFolder)
		# create the job.txt in this folder
		jobFile = open(outFolder+"/job.txt","w")
		# add the current input folders into the job.txt
		for I in inFolders:
			jobFile.write(I + "/maskXXX.tif\n")

		# also add the path to the tracking markers into the job.txt + close the file
		jobFile.write(gtFolder + "/TRA/man_trackXXX.tif")
		jobFile.close()

		print("Prepared " + outFolder)
		cmdFuse = fijiBinary+" --headless --run \"Annotations Merging Tool\" "+\
		          "\"mergeModel=\\\"Threshold - flat weights\\\",filePath=\\\"" + outFolder + "/job.txt\\\",mergeThreshold="\
		          + str(T) + ",fileIdxStr=\\\"" + timesStr + "\\\",outputPath=\\\"" + outFolder + "/maskXXX.tif\\\"\""
		cmdPerf = fijiBinary+" --headless --run \"Cell Tracking Challenge SEG measure\" "+\
		          "\"resPath=\\\"" + outFolder + "\\\",gtPath=\\\"" + gtFolder\
		          + "\\\",fileIdxStr=\\\"" + timesStr + "\\\",optionVerboseLogging=true,optionReportAllResultLabels=false\""

		# run the Annotations merging tool
		fuseMsg = subprocess.run([cmdFuse], shell=True,stdout=subprocess.PIPE,universal_newlines=True)

		# run the SEG measure:
		perfMsg = subprocess.run([cmdPerf], shell=True,stdout=subprocess.PIPE,universal_newlines=True)

		# extract the SEG value obtained with this particular combination
		s = perfMsg.stdout.find("SEG: ")   # find the beginning of the interesting sequence
		S = perfMsg.stdout[s:].find('\n')  # find the end of the interesting sequence (relative to the beginning)

		val = float(perfMsg.stdout[s+5:s+S]) if (s > -1 and S > -1) else -1.0
		print("SEG = "+str(val))

		# log the commands used and reports obtained
		logFile = open(outFolder+"/log_1_commands.txt","w")
		logFile.write(cmdFuse)
		logFile.write("\n")
		logFile.write(cmdPerf)
		logFile.write("\n")
		#
		if val in mapOfBest:
			logFile.write("This SEG result is the same as that of: "+mapOfBest[val].folder+"\n")
		logFile.write(str(val))
		logFile.close()

		logFile = open(outFolder+"/log_2_fusingOutput.txt","w")
		logFile.write(fuseMsg.stdout)
		logFile.close()

		logFile = open(outFolder+"/log_3_measurementOutput.txt","w")
		logFile.write(perfMsg.stdout)
		logFile.close()

		# catalogues the result (while maintaining files of only fixed number of best results)
		AddNextResult(val, CombinationDesc(j,T,outFolder))
