import PySimpleGUI as sg
import csv
import pandas as pd
import helperFunctions as HF
import substitutionHistoryWindow as SHW
import substitutionManualWindow as SMW

#Printing options for Pandas (for debugging)
pd.set_option('display.min_rows', 20)
pd.set_option('display.expand_frame_repr', False)
pd.set_option('max_colwidth', 20)

#This is the main window for the substitution requests interface
def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequestsArchiveFilename,defaultFont=("Courier",11)):

	#read in sub requests from the provided csv filepath
	subRequests=[]
	with open(subRequestsFilename) as f:
		headerLine=f.readline()
		reader = csv.reader(f)
		for row in reader:
			subRequests.append(row+[""])	
			
	#format fields is Requester, Period, Date, Replacement, Reason, Times, CurrAssigned, Errors
	columnWidths=[20,16,16,20,30,50,50,20]

	#Set up layout object that governs what appears in the window
	headerVals=['Requestor','Period(s)','Date(s)','Replacement','Reason','(Frequency) Times','Currently Assigned','Errors']
	row=[sg.Text('APP',size=(4,1)),sg.Text('ACC',size=(4,1)),sg.Text('REJ',size=(4,1)),sg.Text('CAN',size=(4,1))]
	for i in range(len(headerVals)):
		row.append(sg.Text(headerVals[i],expand_x=True,expand_y=True,size=(columnWidths[i],1)))
	layout=[row]
				
	#--------------------------------------------------------------------------
	#Basic error checking before opening display (determines which requests are in red)
	datetimeFreq={}	#track how many requests are for a specific timeslot
	for i in range(len(subRequests)):
		oldID=subRequests[i][1]
		periods=subRequests[i][2].split(';')
		dates=subRequests[i][3].split(';')
		newID=subRequests[i][4]
		for period in periods:
			for date in dates:
				datetime=date+" "+HF.getTimeFromSection(period)
				if datetime in datetimeFreq:
					datetimeFreq[datetime]+=1
				else:
					datetimeFreq[datetime]=1
				if not HF.shiftExists(period,date):
					subRequests[i][6]+="No such shift\n"	#Add to the list of errors with this request
				else:
					if not HF.isAssigned(oldID,period,date):
						subRequests[i][6]+=oldID+" not assigned\n"	#Add to the list of errors with this request
					if newID != "" and HF.isAssigned(newID,period,date):
						subRequests[i][6]+=newID+" already assigned\n"	#Add to the list of errors with this request
					#Test if replacement is already busy with a different section
					if newID != "":
						elsewhere = HF.isBusy(newID,HF.getTimeFromSection(period),date)
						if elsewhere != False:
							subRequests[i][6]+=newID+" assigned elsewhere ("+elsewhere+")\n"	#Add to the list of errors with this request
						
	
	
	#--------------------------------------------------------------------------
	#Formatting the text for the main window
	for i in range(len(subRequests)):
		request=subRequests[i]
		netID=request[1]
		periods=request[2]
		if request[4]=='':
			newSubName="NONE"
		else:
			newSubName=HF.IDToName(request[4])
		others=HF.getAllNamesFromSection(periods,request[3])
		
		times=[]
		for period in periods.split(';'):
			time=HF.getTimeFromSection(period)
			times.append(time)
		
		timeStr="("
		periods=request[2].split(';')
		dates=request[3].split(';')
		for period in periods:
			for date in dates:
				datetime=date+" "+HF.getTimeFromSection(period)
				timeStr+=str(datetimeFreq[datetime])+","
		timeStr=timeStr[:-1]+") "
		for period in periods:
			for date in dates:
				timeStr+=date+" "+str(HF.getTimeFromSection(period))+"; "
		timeStr=timeStr[:-2]
		
		others=str(others)[1:-1]
		others=others.replace("'","")
		
		textVals=[HF.IDToName(netID),*request[2:4],newSubName,request[5],timeStr,others,request[6].strip("\n")]
		
		row=[sg.Column([[sg.Checkbox('',size=(1,1)),sg.Checkbox('',size=(1,1)),sg.Checkbox('',size=(1,1)),sg.Checkbox('',size=(1,1))],[sg.Input("INST. REASON",size=(12,1),key="-INPUT_REASON_"+str(i)+"-")]])]
		if request[6]!="":
			color='#FF9999'
		else:
			color='white'
		for i in range(len(textVals)):
			row.append(sg.Text(textVals[i],expand_x=True,expand_y=True,size=(columnWidths[i],3),text_color=color))
		layout.append(row)
		layout.append([sg.Text('-'*(sum(columnWidths)+27))])
		
	#Adding buttons
	layout.append([sg.Button('Ok'), sg.Button('Cancel'), sg.Button('See Sub History'), sg.Button('Make Manual Changes')])
	layout=[[sg.Column(layout,scrollable=True,expand_y=True,expand_x=True)]]

	# Create the Window
	window = sg.Window('Substitution Requests', layout, font=defaultFont,resizable=True,size=(1900,1000))

	#Event Loop to process "events" and get the "values" of the inputs
	#Cease running if they close the window or press OK or Cancel
	completed=False
	event=-1
	while event != sg.WIN_CLOSED and completed==False:
		event, values = window.read()
		
		#values.values() is an array of the input from all window elements, we use strides to get the checkboxes as "columns" instead of "rows"
		approveValues=list(values.values())[::5]
		acceptValues=list(values.values())[1::5]
		rejectValues=list(values.values())[2::5]
		cancelValues=list(values.values())[3::5]
		
		#Number of approved changes
		nApp = sum([1 for d in approveValues if True == d])
		nAcc = sum([1 for d in acceptValues if True == d])
		nRej = sum([1 for d in rejectValues if True == d])
		nCan = sum([1 for d in cancelValues if True == d])
			
		#event is the most recent "click" from the user
		if event == "See Sub History":
			SHW.subHisWin(subRequestsArchiveFilename,defaultFont)	#open history window
		if event == "Make Manual Changes":
			window['Ok'].Update(disabled=True)	#This is to prevent undefined behavior as a result of manual changes not being represented in the main window
			SMW.subManualWin(subRequestsArchiveFilename,defaultFont)	#open manual change window
			event = "Cancel"	#after we return from the manual change window, just close the whole program
			
		
		if event == 'Ok':
	
			valid=True
			dialogShown = False	#Do not show multiple error dialogues even if there are multiple problems.
			
			#If two responses are both checked in ANY single line, then it is not valid input
			if True in [True for i, j, k, l in zip(approveValues, acceptValues, rejectValues, cancelValues) if ((i and j) or (i and k) or (j and k) or (i and l) or (j and l) or (k and l))]:
				valid=False
				if not dialogShown:
					dialogShown=True
					multiResponseLayout = [[sg.Text('ERROR: You have two different responses checked for at least one substitution')],[sg.Button('Ok')]]
					multiResponseWindow = sg.Window('Multiple Responses Selected',multiResponseLayout,font=defaultFont)
					multiResponseEvent = -1
					while multiResponseEvent != sg.WIN_CLOSED and multiResponseEvent != 'Ok':
						multiResponseEvent,multiResponseValues=multiResponseWindow.read()
					multiResponseWindow.close()
			
			
			allNetIDs = []
			otherReqCache={}
			for i in range(len(subRequests)):
				if approveValues[i] or acceptValues[i]:
					#If any of the app/acc lines have an error, then it is not valid input
					if subRequests[i][6] != "":
						valid=False
						if not dialogShown:
							dialogShown=True
							errorLayout = [[sg.Text('ERROR: You have approved or accepted a request that has an error.\n(See rightmost colum)')],[sg.Button('Ok')]]
							errorWindow = sg.Window('Invalid Request App/Acc',errorLayout,font=defaultFont)
							errorEvent = -1
							while errorEvent != sg.WIN_CLOSED and errorEvent != 'Ok':
								errorEvent,errorValues=errorWindow.read()
							errorWindow.close()
					
					
					#Check that the request doesn't overlap another approved request (as that would create a 'race' condition based on request submission order)
					netID=subRequests[i][1]
					if netID in allNetIDs:#For performance reasons, only check for section/date overlap between requests from the same person (to avoid n^2 searching)
						for j in range(len(otherReqCache[netID])):
							if HF.isRequestOverlap(subRequests[i],otherReqCache[netID][j]):#this request overlaps with a another request by the same person
								valid=False
								if not dialogShown:
									dialogShown=True
									multiRequestLayout = [[sg.Text('WARNING: You have simultaneously approved multiple overlapping requests from '+HF.IDToName(netID)+'.\nThis can produce ambiguous states and is not supported.\nApprove or accept the requests one at a time.')],[sg.Button('Ok')]]
									multiRequestWindow = sg.Window('Multiple Requests Approved',multiRequestLayout,font=defaultFont)
									multiRequestEvent = -1
									while multiRequestEvent != sg.WIN_CLOSED and multiRequestEvent != 'Ok':
										multiRequestEvent,multiRequestValues=multiRequestWindow.read()
									multiRequestWindow.close()
							else:#This request is by the same person but does not overlap
								otherReqCache[netID].append(subRequests[i])
					else:#there are no other outstanding requests by this person
						allNetIDs.append(netID)
						otherReqCache[netID]=[subRequests[i]]
				
				
			#At least one change was approved, check that they didn't click in error:
			if nApp+nAcc+nRej+nCan>0 and valid:		
				verifyLayout = [[sg.Text('Are you sure?\nThis action will make '+str(nApp+nAcc)+' changes to the sectionsDatabase and delete '+str(nApp+nAcc+nRej+nCan)+' sub requests\n***This cannot be undone!***')],[sg.Button('Yes'), sg.Button('No')]]
				verifyWindow = sg.Window('Verify Changes', verifyLayout, font=defaultFont)
				verifyEvent=-1
				while not (verifyEvent == sg.WIN_CLOSED or verifyEvent == 'Yes' or verifyEvent == 'No'):
					verifyEvent, verifyValues = verifyWindow.read()
				verifyWindow.close()
				
				#Apply changes to the database if they chose "yes"
				if verifyEvent == 'Yes':
					trimmedSubrequests = []		#the subRequests after all accepted and rejected have been deleted
					resolvedSubrequests = []	#the subRequests which were aproved/accepted/rejected
					for i in range(len(subRequests)):
						oldID=subRequests[i][1]
						periods=subRequests[i][2].split(';')
						dates=subRequests[i][3].split(';')
						newID=subRequests[i][4]
						for date in dates:
							for period in periods:
								if "HR" in period:
									amount = 0.5
								else:
									amount = 1
								if approveValues[i] or acceptValues[i]:	#If this request was checked "approved" or "accepted"
									HF.reassign(date,period,oldID,newID)	#Actually make the changes in the database
								#HF.incrementSubCount is for tracking how many subs this person requested and what their outcome was
									if newID !="":
										HF.incrementSubCount(newID,4,amount=amount)#Also track how many sub requests were filled by each person
									if approveValues[i]:
										HF.incrementSubCount(oldID,0,amount=amount)
									if acceptValues[i]:
										HF.incrementSubCount(oldID,1,amount=amount)
								if rejectValues[i]:
									HF.incrementSubCount(oldID,2,amount=amount)
								if cancelValues[i]:
									HF.incrementSubCount(oldID,3,amount=amount)
						if (not approveValues[i] and not acceptValues[i] and not rejectValues[i] and not cancelValues[i]):
							trimmedSubrequests.append(subRequests[i][:-1])#Trimmed requests is the list of requests that had no action taken and thus must be re-added to the list of pending requests
						else:
							if approveValues[i]:
								result="APP"
							elif acceptValues[i]:
								result="ACC"
							elif rejectValues[i]:
								result="REJ"
							elif cancelValues[i]:
								result="CAN"
							resolvedSubrequests.append(subRequests[i][:-1]+[result]+[values["-INPUT_REASON_"+str(i)+"-"]])#Resolved requests is kept to be appended to the archived requests database
							
					#If the program crashes during email sending (such as with invalid login) it will deliberately NOT reach the code where it writes to databases
					#KEEP THIS LINE BEFORE THE WRITING STEPS
					HF.generateEmails(resolvedSubrequests)
							
					#Write to the respective databases
					with open(subRequestsFilename,'w', newline='') as f:
						f.write(headerLine)
						writer = csv.writer(f)
						writer.writerows(trimmedSubrequests)
					with open(subRequestsArchiveFilename,'a', newline='') as f:
						writer = csv.writer(f)
						writer.writerows(resolvedSubrequests)
					
					completed=True
							
		elif event == 'Cancel':
			completed=True

	window.close()