import tkinter as tk
from tkinter import filedialog, Text, ttk
import os
import sys
import hoare_logic
import subprocess
import shutil


class GUI:

	def __init__(self):
		self.pythonFiles = []
		self.conditionFilename = ''
		self.output = ''

		self.root = tk.Tk()
		self.root.title('Python Tracer')

		self.canvas = tk.Canvas(self.root, height=700, width=1400, bg='#263D42')
		self.canvas.pack(fill="both", expand=True)

		self.frame = tk.Frame(self.root, bg='white')
		self.frame.place(relwidth=0.2, relheight=0.15)

		# window for tabs
		self.tabWindow = tk.Frame(self.root, bg='white')
		self.tabWindow.place(relwidth=0.8, relheight=1.0, relx=.2)

		# allow creation of tabs
		self.my_notebook = ttk.Notebook(self.tabWindow)
		self.my_notebook.pack()

		# create tab windows
		self.codeWindow = tk.Frame(self.my_notebook, width=1120, height=700, bg='white')
		self.outputWindow = tk.Frame(self.my_notebook, width=1120, height=700, bg='white')
		self.fileWindow = tk.Frame(self.my_notebook, width=1120, height=700, bg='white')

		# have tabs fill any empty space available
		self.codeWindow.pack(fill='both', expand=1)
		self.outputWindow.pack(fill='both', expand=1)
		self.fileWindow.pack(fill='both', expand=1)

		# add tabs to window
		self.my_notebook.add(self.fileWindow, text='Files')
		self.my_notebook.add(self.codeWindow, text='Code')
		self.my_notebook.add(self.outputWindow, text='Output')
		

		# text box to be filled with code
		# disabled state prevents user from editing code
		self.codeDisplay = tk.Text(self.codeWindow, width=1120, height=700, bg='white', state=tk.DISABLED)
		self.codeDisplay.pack()

		# text box to be filled with output
		self.outputDisplay = tk.Text(self.outputWindow, width=1120, height=700, bg='white', state=tk.DISABLED)
		self.outputDisplay.pack()

		# window to place file names and buttons
		self.fileDisplay = tk.Frame(self.fileWindow, width=1120, height=700, bg='white')
		self.fileDisplay.pack()
		self.file_list = FileList(self)

		# button and label to upload condition file
		self.openConditionsButton = tk.Button(self.frame, text="Open File", fg='white', bg='#263D42', command=self.addConditions)
		self.openConditionsButton.grid(row=0,column=0)
		self.openConditionsLabel = tk.Label(self.frame, text="Choose Conditions File", bg="white", padx=5)
		self.openConditionsLabel.grid(row = 0, column = 1)
		self.deleteButton = tk.Button(self.frame, text='X', fg='red', bg='white', command=self.deleteConditions)


		# button and label to upload python file(s)
		self.openPythonButton = tk.Button(self.frame, text="Open File", fg='white', bg='#263D42', command=self.addPython)
		self.openPythonButton.grid(row=1,column=0)
		self.openPythonLabel = tk.Label(self.frame, text="Choose Python File", bg="white", padx=5)
		self.openPythonLabel.grid(row = 1, column = 1)

		# button to run conditions file
		self.runButton = tk.Button(self.frame, text="Run", fg='white', bg="#263D42", command=self.check)
		self.runButton.grid(row=2, column=1)
		# continue to render
		self.root.mainloop()

	def addPython(self):
		
		# open .py file
		cwd = os.getcwd()
		pythonFilename = filedialog.askopenfilename(initialdir=cwd+'/test-cases/fibonacci', title="Add Python File(s)", filetypes=(("python", "*.py"), ("all files", "*.*")))
		if  pythonFilename != '':
			self.pythonFiles.append(pythonFilename)

			
			# allow text widget to be edited
			self.codeDisplay.config(state=tk.NORMAL)
			# clear text for new file
			self.codeDisplay.delete('1.0', tk.END)
			# insert code
			with open(pythonFilename, 'r') as f:
				self.codeDisplay.insert(tk.END, f.read())	
			# no longer allow text widget to be edited
			self.codeDisplay.config(state=tk.DISABLED)
			self.openPythonLabel.config(text="Choose Python File")
			self.file_list.insert(pythonFilename)



	def addConditions(self):
		cwd = os.getcwd()
		conditionFilename = filedialog.askopenfilename(initialdir=cwd+'/test-cases/fibonacci', title="Select Condition File", filetypes=(("text", "*.txt"), ("all files", "*.*")))
		self.conditionFilename = conditionFilename
		# if user chooses a file
		if conditionFilename != '':
			# update label
			self.openConditionsLabel.config(text=conditionFilename.split('/')[-1])
			# delete buttons not necessarily needed with the files tab
			self.deleteButton = tk.Button(self.frame, text='X', fg='red', bg='white', command=self.deleteConditions)
			self.deleteButton.grid(row=0, column=2)
			self.deleteButton.config(state='normal')
			with open(conditionFilename, 'r') as f:
				self.codeDisplay.insert(tk.END, f.read())
			self.openConditionsButton.config(state='disabled')
			self.file_list.insert(conditionFilename)

	def deleteConditions(self):
		self.outputDisplay.config(state=tk.NORMAL)
		self.openConditionsLabel.config(text='Select Condition File')
		self.outputDisplay.delete('1.0', tk.END)
		self.outputDisplay.config(state=tk.DISABLED)
		self.deleteButton.config(state='disabled')
		self.deleteButton.destroy()
		self.openConditionsButton.config(state='normal')

	def viewCode(self, fileName):
		self.codeDisplay.config(state=tk.NORMAL)
		self.codeDisplay.delete('1.0', tk.END)
		with open(fileName, 'r') as f:
			self.codeDisplay.insert(tk.END, f.read())
		self.codeDisplay.config(state=tk.DISABLED)
		self.my_notebook.select(1)
		

	def viewOutput(self, fileName):
		self.outputDisplay.config(state=tk.NORMAL)
		self.outputDisplay.delete('1.0', tk.END)
		if fileName is self.conditionFilename:
			for name, output in self.output.items():
				self.outputDisplay.insert(tk.END, name + ':\n' + output + '\n')
		else:
			self.outputDisplay.insert(tk.END, fileName.split('/')[-1] + ':\n' + self.output[fileName.split('/')[-1]])
		self.outputDisplay.config(state=tk.DISABLED)
		self.my_notebook.select(2)

		self.openPythonLabel.config(text="Choose Python File")

	def check(self):
		"""
		Check if files exist
		if self.conditionFilename == '' or os.path.exists(txt_path) == False:
			messagebox.showinfo('Info', 'Not found txt file, please select it again.')
			return

		python_path = self.python_path.get()
		if python_path == '' or os.path.exists(python_path) == False:
		    messagebox.showinfo('Info', 'Not found python file, please select it again.')
		    return
		"""

		# Clean all text in result
		self.outputDisplay.config(state=tk.NORMAL)
		self.outputDisplay.delete('1.0', tk.END)

		# Copy all files into tmp dir
		if os.path.exists('tmp'):
			shutil.rmtree('tmp', True)
		os.mkdir('tmp')
		shutil.copy(self.conditionFilename, 'tmp')
		for pythonFile in self.pythonFiles:
			shutil.copy(pythonFile, 'tmp')
		shutil.copy('hoare_logic.py', 'tmp')

		# Go to the tmp folder and run the code and get result
		os.chdir('tmp')
		msg = {}
		for pythonFile in self.pythonFiles:
			target_path = os.path.basename(pythonFile)
			cmd = ['python3', target_path]
			process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
			
			if process.stdout:
				while True:
					out = process.stdout.readline().decode('UTF-8')
					if out == '' and process.poll() is not None:
						break
					if out != '':
						self.outputDisplay.insert(tk.END, out)
						self.codeDisplay.config(state=tk.DISABLED)
						msg[pythonFile.split('/')[-1]]=(out)

			if process.stderr:
				while True:
					err = process.stderr.readline().decode('UTF-8')
					if err == '' and process.poll() is not None:
						break
					if err != '':
						print('stderr: %s' % err)
						errmsg.append(err)
						self.outputDisplay.insert(tk.INSERT, err + '\n')
		self.output = msg
		# Back to origin folder
		os.chdir('..')

		# Delete the tmp folder
		shutil.rmtree('tmp', True)
		



class File():
	def __init__(self, fileName):
		self.fileName = ''
		self.viewButton = None
		self.outputButton = None
		self.deleteButton = None
		self.fileLabel = None
	

class FileList():
	def __init__(self, GUI):
		self.row = 0
		self.gui = GUI
		self.files = []

	def insert(self, fileName):
		if fileName not in self.files:
			new_file = File(fileName)
			new_file.viewButton = tk.Button(self.gui.fileDisplay, text='View Code', fg='black', bg='gray', command=lambda: self.gui.viewCode(fileName))
			new_file.outputButton = tk.Button(self.gui.fileDisplay, text='View Output', fg='black', bg='gray', command=lambda: self.gui.viewOutput(fileName))
			new_file.deleteButton = tk.Button(self.gui.fileDisplay, text='X', fg='red', bg='gray', command=lambda: [new_file.viewButton.destroy(), new_file.outputButton.destroy(), 
																													new_file.deleteButton.destroy(), new_file.fileLabel.destroy(),
																													self.gui.pythonFiles.remove(fileName), self.gui.output.pop(fileName.split('/')[-1])])
			new_file.fileLabel = tk.Label(self.gui.fileDisplay, text=fileName, bg='white')
			new_file.fileLabel.grid(row=self.row, column=0)
			new_file.viewButton.grid(row=self.row, column=1)
			new_file.outputButton.grid(row=self.row, column=2)
			new_file.deleteButton.grid(row=self.row, column=3)
			self.files.append(fileName)
			self.row += 1

		else:
			self.gui.openPythonLabel.config(text='File Already Loaded')



if __name__ == '__main__':
	gui = GUI()