#Created by dan december 2011.  www.danomagnum.com
#from Tkinter import Tk, Frame, BOTH, RIGHT, LEFT, RAISED, Label, IntVar, BooleanVar, X, RAISED, Menu, Scrollbar,Y, Scrollbar, TclError, N, S
from Tkinter import *
#from ttk import Button, Style, Scale
#from ttk import Scale
import tkinterladder as ladder
#from .ladder import tkinterladder as ladder
import time
import pickle
import copy
import tkFileDialog
import tkSimpleDialog
import tkxmltoladder as xmltoladder
import datetime

FAULTDEBUG = FALSE

FORMATS = [('Python Ladder Logic','*.pll')]
FORMATSXML = [('Python Ladder Logic XML files','*.xml')]

class AutoScrollbar(Scrollbar):
	# a scrollbar that hides itself if it's not needed.  only
	# works if you use the grid geometry manager.
	def set(self, lo, hi):
		if float(lo) <= 0.0 and float(hi) >= 1.0:
				# grid_remove is currently missing from Tkinter!
				self.tk.call("grid", "remove", self)
		else:
				self.grid()
		Scrollbar.set(self, lo, hi)
	def pack(self, **kw):
		raise TclError, "cannot use pack with this widget"
	def place(self, **kw):
		raise TclError, "cannot use place with this widget"

class IOWindow(tkSimpleDialog.Dialog):
	def body(self, master):

		self.vartype = StringVar()
		Label(master, text="Type:").grid(row=0)
		Radiobutton(master, text="Boolean", variable=self.vartype, value='bool').grid(row=0,column=1)
		Radiobutton(master, text="Integer", variable=self.vartype, value='int').grid(row=0,column=2)
		Radiobutton(master, text="String", variable=self.vartype, value='str').grid(row=0,column=3)
		
		self.vartype.set('bool')


		Label(master, text="Name:").grid(row=1)
		self.name = Entry(master)
		self.name.grid(row=1, column=1)

		Label(master, text="Value:").grid(row=2)
		self.value = Entry(master)
		self.value.insert(0,0)
		self.value.grid(row=2, column=1)

		self.result = None
		return self.name # initial focus

	def apply(self):
		val = False
		vartype = self.vartype.get()
		print vartype
		val2 = self.value.get()
		if vartype == 'bool':
			val = bool(val2)
		elif vartype == 'int':
			val = int(val2)
		elif vartype == 'str':
			val = str(val2)
		print val
		self.result = [val, vartype,self.name.get()]
#print first, second 

class FaultWindow(tkSimpleDialog.Dialog):

	def __init__(self, parent,faults = [], title = 'Fault'):

		Toplevel.__init__(self, parent)
		self.transient(parent)
		self.faults = faults

		if title:
			self.title(title)

		self.parent = parent

		self.result = None

		body = Frame(self)
		self.initial_focus = self.body(body)
		body.pack(padx=5, pady=5,fill=BOTH,expand=1)

		self.buttonbox()

		self.grab_set()

		if not self.initial_focus:
			self.initial_focus = self

		self.protocol("WM_DELETE_WINDOW", self.cancel)

		self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
					  parent.winfo_rooty()+50))

		self.initial_focus.focus_set()

		self.wait_window(self)


	def body(self, master):

		Label(master, text="Faults:").pack(side=TOP)
		self.faultlist = Listbox(master)
		self.faultlist.pack(side=TOP,fill=BOTH,expand=1)
		for f in self.faults:
			self.faultlist.insert(END,f)

		self.result = None
		return self.faultlist

	def apply(self):
		pass
#print first, second 



class MainWindow(Frame):
	def __init__(self, parent):
		Frame.__init__(self, parent, background="white")
		self.parent = parent
		self.rungs = []
		self.exspeed = 100
		self.paused = False
		self.pausedvar = BooleanVar()
		self.faulted = False
		self.faultvar = BooleanVar()
		self.faults = []
		self.pausedvar.set(self.paused)
		self.faultvar.set(self.faulted)
		self.initUI()
		self.updatesize = False
		self.calculating = False

	def initUI(self):
		#this sets up the interface, and then goes into the program part
		#self.parent.title("PyLadderLogic")
		#self.style = Style()
		#self.style.theme_use("default")
		#self.pack(fill=BOTH,expand=1)
		self.grid(row=0,column=0,sticky='news')
		ladder.fix_scroll = self.scrollsizeupdate
		ladder.remove_row = self.removerows

		#sc = Scrollbar(self)
		#sc.pack(side=RIGHT,fill=Y)

		topperfrm = Frame(self)
		topperfrm.pack(fill=X,expand=1)
		spdlbl = Label(topperfrm,text="Speed^-1")
		spdlbl.pack(side=LEFT)
		scale = Scale(topperfrm, from_=50, to=1000, command=self.onScale,orient=HORIZONTAL)
		scale.set(100)
		scale.pack(side=LEFT)

		spdlbl = Checkbutton(topperfrm,text="Pause",command=self.playpause, variable=self.pausedvar,indicatoron=0)
		spdlbl.pack(side=RIGHT)
		self.faultbutton = Checkbutton(topperfrm,text="Fault",command=self.clearfault, variable=self.faultvar,indicatoron=0,fg='black')

		self.faultbutton.pack(side=RIGHT)
		button1 = Button(topperfrm,text="Run one loop",command=self.runone)
		button1.pack(side=RIGHT)
		button1 = Button(topperfrm,text="Update",command=self.toggleit)
		button1.pack(side=RIGHT)



		topfrm = Frame(self)
		topfrm.pack(fill=X,expand=1)



		self.popup = Menu(root,tearoff=0)
		self.popup.add_command(label='Add Rung',command=self.addrung)
		self.popup.add_command(label='Add IO',command=self.addIO)
		self.popup.add_command(label='Load XML',command=self.loadxml)
		self.popup.add_command(label='Save XML',command=self.savexml)
		root.config(menu=self.popup)


		#this section defines the "bits" in the "plc" and a few 10-bit words
		#IO = ladder.value_word(0,"I0:0")
		#IO.bits_display_new(topfrm)
		#self.IO = IO.bits()
		self.IO = []
		#self.IO[0].set(True)
		#self.IO[1].set(True)
		#self.IO[2].set(True)
		#self.IO[6].set(True)
		#ladder.itemlist += self.IO

		self.dataforms = [topfrm]

	def addrung(self):
		r = ladder.rung()
		self.rungs.append(r)
		r.display_new(self)


	def addIO(self):
		w = IOWindow(self)
		if w.result is not None:
			[value, vartype, name] = w.result
			r = ladder.IO(value, name, vartype)
			self.IO.append(r)
			r.display_new(self.dataforms[0])
			ladder.itemlist = self.IO

	def toggleit(self):
		#this function is named this because I just kept reusing it, so no reason.
		#self.bv.set(random.randint(0,1023))
		#self.bv.display_update()
		#self.blk1.display_update()
		#for r in self.rungs:
			#r.display_update()
		#self.update_idletasks()
		#canvas.config(scrollregion=canvas.bbox("all"))
		self.removedata()

	def runone(self):
		#do one run through the ladder
		self.paused = True
		self.pausedvar.set(self.paused)
		for r in self.rungs:
			r.run()
			r.display_update()
		
	def onScale(self, val):
		#set the speed
		speed = int(float(val))
		self.exspeed = int(float(val))

	def playpause(self):
		#toggle the play/pause
		self.paused = not self.paused
		self.pausedvar.set(self.paused)

	def clearfault(self):
		#clears any faults
		w = FaultWindow(self,self.faults)
		self.faulted = False
		self.faultvar.set(self.faulted)
		self.faultbutton.configure(fg='black')


	def scrollsizeupdate(self):
		#canvas.config(scrollregion=canvas.bbox("all"))
		self.updatesize=True

	def removerows(self,row):
		pausestate = self.paused
		self.paused = True
		self.pausedvar.set(self.paused)
		while self.calculating:
			time.sleep(.1)
		self.rungs.remove(row)
		row.__del__()
		self.paused = pausestate
		self.pausedvar.set(self.paused)

	def removedata(self):
		pausestate = self.paused
		self.paused = True
		self.pausedvar.set(self.paused)
		while self.calculating:
			time.sleep(.1)
		self.IO = []
		ladder.itemlist = []
		dfs = self.dataforms[:]
		for f in dfs:
			f.destroy()
		self.dataforms = []
		self.paused = pausestate
		self.pausedvar.set(self.paused)

	def savexml(self):
		filename = tkFileDialog.asksaveasfilename(parent=root,filetypes=FORMATSXML,title="Save")
		if not filename:
			return False
		
		pausestate = self.paused
		self.paused = True
		self.pausedvar.set(self.paused)

		while self.calculating:
			time.sleep(.1)


		xmltoladder.create_xml(self.rungs,self.IO,filename)
		


		self.paused = pausestate
		self.pausedvar.set(self.paused)



	def loadxml(self):
		filename = tkFileDialog.askopenfilename(parent=root,filetypes=FORMATSXML,title="Save")
		if not filename:
			return False
		rows = self.rungs[:]
		for r in rows:
			self.removerows(r)
		self.removedata()
		f = open(filename,'r')
		[self.rungs, self.IO] = xmltoladder.create_ladder(f)
		f.close()
		self.reinit()
		self.updatesize=True

	def reinit(self):
		ladder.itemlist = []
		topfrm = Frame(self)
		topfrm.pack(fill=X,expand=1)
		for i in self.IO:
			i.display_new(topfrm)
		ladder.itemlist += self.IO
		self.dataforms.append(topfrm)


		for r in self.rungs:
			r.display_new(self)
			r.display_update()


def calculatron(*args):
	#this is what automatically executes the logic at whatever interval is set
	app = args[0]
	if not any((app.paused, app.faulted)):
		app.calculating = True
		for r in app.rungs:
			try:
				r.run()
				r.display_update()
			except Exception as e:
				app.faulted = True
				app.faultvar.set(True)
				app.faultbutton.configure(fg='red')
				app.faults.append(e.message)
				if FAULTDEBUG:
					raise
		app.calculating = False
	else:
		pass
	root.after(app.exspeed,calculatron,app)
def scrollbarupdate(*args):
	app = args[0]
	if app.updatesize:
		canvas.config(scrollregion=canvas.bbox("all"))
		app.updatesize = False
	root.after(100,scrollbarupdate,app)

def OnMouseWheel(event):
	d = 0
	if event.num == 5 or event.delta == -120:
		d = 1
	elif event.num == 4 or event.delta == 120:
		d = -1
	canvas.yview("scroll", d, "units")

root = Tk()
vscrollbar = AutoScrollbar(root)
vscrollbar.grid(row=0,column=1, sticky=N+S)
hscrollbar = AutoScrollbar(root, orient=HORIZONTAL)
hscrollbar.grid(row=1, column=0, sticky=E+W)
canvas = Canvas(root, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set,width=1000,height=300)
canvas.grid(row=0, column=0, sticky=N+S+E+W)

root.bind('<MouseWheel>', OnMouseWheel)
root.bind('<Button-4>', OnMouseWheel)
root.bind('<Button-5>', OnMouseWheel)

vscrollbar.config(command=canvas.yview)
hscrollbar.config(command=canvas.xview)

root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0,weight=1)

frame = Frame(canvas)
frame.rowconfigure(1, weight=1)
frame.columnconfigure(1,weight=1)


#rows = 5
#for i in range(1,rows):
    #for j in range(1,10):
        #button = Button(frame, padx=7, pady=7, text="[%d,%d]" % (i,j))
        #button.grid(row=i, column=j, sticky='news')


app = MainWindow(frame)
canvas.create_window(0, 0, anchor=NW, window=frame)
frame.update_idletasks()
canvas.config(scrollregion=canvas.bbox("all"))
app.update_idletasks()
root.after(100,calculatron,app)
root.after(100,scrollbarupdate,app)
root.mainloop()
