"""
Folder watcher used for smart imaging
It first pop up a user input to choose the method to use for smart imaging anf if we are in a test mode (in this case we have to define an image folder and the job files will be saved in a subfolder)
Then it calls the corresponding script for smart imaging that pops up another user input window to configure this selected method
Once this configuration is closed it will watch the folder for new images to come, perform the smart imaging detection and will write the job file acccordingly until the max number of images set in the configuration window is reached (or until the macro is killed, otherwise the folder watching keeps going on)

The value of the user input are also saved to a temporary file of the same name than the script, in order to be recalled at next execution.
"""
import os, time
from os.path          import join,isdir,isfile
from ij.IJ            import log
from fiji.util.gui    import GenericDialogPlus
from java.lang.System import getProperty
from ast              import literal_eval

# Try to recover previous input from tmp file
Path_tmp = join(getProperty('fiji.dir'),'scripts','Plugins','Acquifer','Smart_Imaging.tmp')

if isfile(Path_tmp):
	try:
		# open file in read mode
		tmpFile = open(Path_tmp,'r')
		Lines = [line.rstrip() for line in tmpFile] # rstrip permettant de virer le \n
		tmpFile.close()

		Method0           = Lines[0]
		CountMax0         = eval(Lines[1])
		Test0             = literal_eval(Lines[2])
		doJob0            = literal_eval(Lines[3])
		HighObj_Mag0      = Lines[4]
		Path_ImageFolder0 = Lines[5]
		Script0           = '\n'.join(Lines[6:]) # join further lines into one paragraph
		
	except:
		pass

else: # No file or bug, then use default values
	Method0   = "Template matching"
	CountMax0 = 96
	Test0     = True
	doJob0    = True
	HighObj_Mag0      = "10x"
	Path_ImageFolder0 = "ImageFolder"
	Script0           = """SetLight(6,10,10,0.000)
AcquireAutofocus(7,100.000,2,'True','True')
SetLight(6,50,10,0.000,'Flash')
AcquireAutofocus(6,20.000,2,'True','True')
SetLight(6,50,10,0.000)
Acquire(1,0.000,1,'D:\IMAGING-DATA\IMAGES\SCRIPTS','True','SIdata')
"""
 		
# Input GUI
Win = GenericDialogPlus("Smart Imaging")

Win.addChoice("Smart imaging method",["Template matching","Keypoint matching","Center of mass"],Method0)
Win.addNumericField("Number of well to process",CountMax0,0)
Win.addCheckbox("Test mode (if not on the microscope)",Test0)
Win.addDirectoryField("Directory to watch (in test mode)",Path_ImageFolder0)
Win.addCheckbox("Write job files", doJob0)                                    # JobFile are written anyway if we are not in Test mode
Win.addChoice("Objective for smart imaging",["2x","4x","10x","20x"],HighObj_Mag0)


# Add a text field with the smart imaging script
Win.addMessage("Copy/Paste a smart imaging script in the field below or use a template : ")
Win.addTextAreas(Script0,None,5,100)


# Add help button
Disclaimer = '''<html><p>
				The smart imaging is the implementation of feedback microscopy on Acquifer's imaging machine.<br>   
				The idea is to use image recognition scripts to automatically detect the ROI in the low magnification images,<br>
				and perform the corresponding high magnification acquisition. See <a href='https://github.com/acquifer/ImageInLife/wiki#roi-detection'>documentation.</a>
				</p></html>
			'''
Win.addHelp(Disclaimer)
Win.showDialog()

if Win.wasOKed():
	
	# Recover inputs
	Method           = Win.getNextChoice()
	CountMax         = Win.getNextNumber()
	Test             = Win.getNextBoolean()
	doJob            = Win.getNextBoolean()
	HighObj_Mag      = Win.getNextChoice()
	Path_ImageFolder = Win.getNextString()
	Script           = Win.getNextText()

	# Save the inputs to a temporary file for further use. Overwritten every time
	tmpFile = open(Path_tmp,'w') # open in write mode this time
	
	tmpFile.write(Method+'\n')
	tmpFile.write(str(CountMax)+'\n')
	tmpFile.write(str(Test)+'\n')
	tmpFile.write(str(doJob)+'\n')
	tmpFile.write(HighObj_Mag+'\n')
	tmpFile.write(Path_ImageFolder+'\n')
	tmpFile.write(Script)

	tmpFile.close()
	
	
	# Set the run method according to choice
	if Method == "Template matching":
		from Match_Template_SI_IJM import Run, Config
	
	elif Method == "Keypoint matching" :
		from KeypointMatching import Run, Config
	
	elif Method == "Center of mass":
		from CenterOfMass import Run, Config
	
	# Pop up a user input window to configure the smart imaging method chosen
	Config()
	
	# I - IMAGE FOLDER
	if not Test: # We are really doing smart imaging
	
		Path_LocationFile = r"D:\IMAGING-DATA\JOBS\Image_Location.pth"
		JobFolder         = r"D:\IMAGING-DATA\JOBS"
	
		#Try to read the LocationFile to get the image folder
		FoundLocation = False
		while not FoundLocation :
		
		    time.sleep(1) # 1sec delay - prevent loop to run too fast and overload processes
		
		    try :
		        LocationFile     = open(Path_LocationFile,'r')
		        Path_ImageFolder = LocationFile.readline().rstrip() # read content of file and remove new line character
		        LocationFile.close()
		        os.remove(LocationFile) # delete file once read
		        
		        FoundLocation = True
		        print 'Found image location :', Path_ImageFolder
		        
		    except Exception, Error : # File not existing yet
		        print Error
		        FoundLocation = False
	
	else: # in test mode
		#Path_ImageFolder = Path_ImageFolder.getCanonicalPath() # Convert from Java file to its path
		#Path_ImageFolder = Path_ImageFolder
		
		if doJob :
			JobFolder = join(Path_ImageFolder,"JOBS")
			if not os.path.exists(JobFolder): 
				os.mkdir(JobFolder)
	
	
	
	# II - Create function that will write the job files
	
	# correspondance used to write the job file
	PixToObj = {'32500':1, '16250':2, '6500':3, '3250':4} # map pixel size from the filename to objective index
	ObjToPix = {v: k for k, v in PixToObj.iteritems()}    # invert mapping
	
	MagToObj       = {'2x':1, '4x':2, '10x':3, '20x':4} 	  # Mapping magnification to its objective index
	ObjToMag       = {v: k for k, v in MagToObj.iteritems()}  # Mapping index to magnification
	
	ObjIdx_High    = MagToObj[HighObj_Mag]              	  # Get objective index from magnification for SI given by user 
	PixelSize_High = ObjToPix[ObjIdx_High]                    # Get Pixel size from obj index
	
	
	def WriteJob(ImageName,X,Y):
		'''
		Function that will be call with each image after detection of the region of interest
		X,Y     : Objective coordinates in mm with 3 decimal digits for high objective imaging
		'''
		
		# Extract Metadata before writing job file - IM4 name convention here !
		Well           = ImageName[1:5]     # A1 for instance
		WellNum        = int(ImageName[106:111]) # 1 to 96, convert to int to remove leading 0
		PixelSize_Low  = ImageName[34:39]
		ObjIdx_Low     = PixToObj[PixelSize_Low]  # Get objective index from pixel size read from image name
		Obj_Low        = ObjToMag[ObjIdx_Low]     # Magnification '2X','4X','10X' or '20X'
		
		#Power       = int(ImageName[43:47])
		#Integration = int(ImageName[51:55])

				
		# Write job file for higher magnification for each processed image
		print 'Writing job file'
		
		Path_TmpFileName = os.path.join(JobFolder, ImageName+".tmp") # first created as a tmp file to prevent machine from reading itthen rename as .job 
		
		JobFile = open(Path_TmpFileName,'w') 
		JobFile.write("SetWellNo({})\n".format(WellNum))
		JobFile.write("SetMeta('Well Coordinate','{}')\n".format(Well))
		JobFile.write("SetMeta('Well Pos in Well','1')\n")            
		JobFile.write("GotoXY({:.3f},{:.3f},'Abs')\n".format(X,Y)) # x,y as float 3-decimal, force the trailing 0 if 0 as last decimal 
		
		JobFile.write("SetMeta('Objective','{}')\n".format(HighObj_Mag)) # '2X','4X','10X' or '20X'
		JobFile.write("SetMeta('Objective Pixel Size','{}')\n".format(PixelSize_High))
		JobFile.write("SetObjective({})\n".format(ObjIdx_High))  
		
		'''
		JobFile.write("SetLight(6,10,10,0.000)\n")
		JobFile.write("AcquireAutofocus(7,100.000,2,'True','True')\n")
		JobFile.write("SetLight(6,50,10,0.000,'Flash')\n")
		JobFile.write("AcquireAutofocus(6,20.000,2,'True','True')\n")
		JobFile.write("SetLight(6,50,10,0.000)\n")
		JobFile.write("Acquire(1,0.000,1,'D:\IMAGING-DATA\IMAGES\SCRIPTS','True','SIdata')\n") # Images will be saved in a subfolder SIdata
		'''
		JobFile.write(Script+'\n')
		
		# Switching off the light and going back to previous obj.
		JobFile.write("SetLight(0,0,0,0)\n")
		JobFile.write("SetMeta('Objective','{}')\n".format(Obj_Low)) 
		JobFile.write("SetMeta('Objective Pixel Size','{}')\n".format(PixelSize_Low)) 
		JobFile.write("SetObjective({})".format(ObjIdx_Low)) # no new line here last line to write !
		JobFile.close()
		
		Path_JobFileName = Path_TmpFileName[:-4]+'.job'
		
		# Check that the job file is not already existing
		if isfile(Path_JobFileName):
			log('Overwriting existing JobFile')
			print 'Overwriting existing JobFile'
			os.remove(Path_JobFileName)		
		
		# Finally rename from .tmp to .job 
		os.rename(Path_TmpFileName,Path_JobFileName)
	
	
	# III - FOLDER WATCHING and calling the Run method from the imported script
	before = os.listdir(Path_ImageFolder) # initialisation must be out of the while loop since we are updating it with "current" at the end of the loop
	
	# INITIALISATION - We process the image present in the folder before ImageLocation.pth is created (currently this is the case, some image are saved before the file is created)
	
	count = 0  # initialise count of processed images
	
	for FileName in before : # images in test folder (if test mode) or written before the ImageLocation.pth
	 	
		if count == CountMax :
			log('')
			log('DONE - Processed all images')
			
			print ''
			print 'DONE - Processed all images'
			break
	
		else :
			FilePath = join(Path_ImageFolder,FileName)
			
			if not isdir(FilePath):       # Check that it is a file and not a folder
				# Report progress
				log('')
				log('Process '+FileName)
				print 'Process ', FileName
	
				# Call Run and write job file
				X,Y = Run(FilePath)       # call the Run method from imported module using the image as parameter
				if doJob or not Test : WriteJob(FileName,X,Y)
				count += 1			      # update count of processed image
	
	
	# CONTINUATION
	Delay = 5  # delay in sec between successive scans for new image
	while count < CountMax : # strictly inferior since count starts at 0, and we update it after processing the image ie once we process the last image the counter will be CountMax and we dont want to run another round
	#while True: # only Keyboard interrupt version
		
	    time.sleep(Delay) # before_List was updated just previously at the end of the loop so delay prior to a new listdir scan 
	    current = os.listdir(Path_ImageFolder)
	    new = list( set(current) - set(before) ) # list containing new file and folder name in Path_ImageFolder
	    
	    if new != []: # New Files have appeared - PUT THE CODE TO EXECUTE HERE
	        for FileName in new :
				FilePath = join(Path_ImageFolder,FileName)
		
				if not isdir(FilePath):       # Check that it is a file and not a folder
					log('') # skip a line
					log('Process '+FileName)
					print ''
					print 'Process', FileName
					X,Y = Run(FilePath)       # call the Run method from imported module (Template matching or other) using the image as parameter
					if doJob or not Test : WriteJob(FileName,X,Y)
	
	    			# Update count of processed images
	    			count += 1
	    			if count == CountMax : 
						log('')
						log('DONE - Processed all images')
	    				
						print ''
						print 'DONE - Processed all images'
	    			
	    else: # No new files, keep runnning the loop
	        pass
	     
		
	    # update the 'history' of the folder watcher, and going for another round of while (except if we reached the count)
	    before = current
