#Created by dan december 2011.  www.danomagnum.com
#from ttk import Label, Button#, Checkbutton, Style
from Tkinter import StringVar, LEFT, RIGHT, BOTH, Frame, X, RAISED, IntVar, Menu, Checkbutton, Y, BooleanVar, Label, Button
import tkMessageBox
import tkSimpleDialog
import block_functions
import ladder

TRUECOLOR = "green"
FALSECOLOR = "red"
FAULTCOLOR = "yellow"
LATCHCOLOR = "blue"
DEFAULTCOLOR = "black"
fix_scroll = lambda x:x
remove_row = lambda x:x


functionlist = block_functions.BLOCK_FUNCTION_LIST
itemlist = [] #need to fill this in to add stuff I guess

class IO(ladder.IO):
	def __init__(self,value,name,typedesc,input=False,description=""):
		super(IO,self).__init__(value,name,typedesc,input,description)

		self.displayvariable = StringVar()
		if self.typedesc == 'bool':
			self.displayvalue = BooleanVar()
		elif self.typedesc == 'int':
			self.displayvalue = IntVar()
		else:
			self.displayvalue = StringVar()
		self.display = []

	def prompt_for_value(self):
		if self.typedesc == 'int':
			result = tkSimpleDialog.askinteger(self.name,self.name,initialvalue=self.value,minvalue=0,maxvalue=1023)
			if result is not None:
				self.set(result)
			self.display_update()

	def delifparent(self,parent):
		displays = self.display[:]
		for d in displays:
			if d.master == parent:
				#I am a child of you, so I wll delete myself
				self.display.remove(d)
				d.destroy()
		if not self.display:
			del(self)


	def latchon(self):
		super(IO,self).latchon()
		self.display_update()

	def latchoff(self):
		super(IO,self).latchoff()
		self.display_update()


	def unlatch(self):
		super(IO,self).unlatch()
		self.display_update()

	def clicked(self):
		#super(IO,self).clicked()
		self.toggle_and_display()
	def toggle_and_display(self):
		self.toggle()
		self.display_update()

	def display_new(self,parent):
		self.display_update()
		b = Checkbutton(parent,textvariable=self.displayvariable,command=self.clicked,variable=self.displayvalue,indicatoron=0)
		b.pack(side=LEFT)
		self.display.append(b)

		self.popup = Menu(parent,tearoff=0)
		self.popup.add_command(label="force on",command=self.latchon)
		self.popup.add_command(label="force off",command=self.latchoff)
		self.popup.add_command(label="remove force",command=self.unlatch)
		self.popup.add_separator()
		self.popup.add_command(label="properties",command=self.properties_display)
		self.popup.add_command(label="Set",command=self.prompt_for_value)
		self.popup.add_command(label="description",command=self.setdesc)
		b.bind("<Button-3>",self.popup_menu)

		return b

	def popup_menu(self,event):
		try: 
			self.popup.tk_popup(event.x_root,event.y_root,0)
		finally:
			self.popup.grab_release()

	def properties_display(self):
		ds = self.properties()
		tkMessageBox.showinfo(self.name,ds)


	def display_update(self):
		if self.display:
			for d in self.display:
				if self.latched:
					d.configure(foreground=LATCHCOLOR)
				else:
					d.configure(foreground=DEFAULTCOLOR)
		ds = self.name + "\n" + str(self.value)
		if self.description:
			ds += "\n" + self.description
		if self.displayvariable:
			self.displayvariable.set(ds)

			dv = None
			if self.typedesc == 'bool':
				dv = bool(self.value)
			elif self.typedesc == 'int':
				dv = int(self.value)
			else:
				dv = str(self.value)
			self.displayvalue.set(dv)

	
	def setdesc(self):
		result = tkSimpleDialog.askstring(self.name,self.name,initialvalue=self.description)
		if result is not None:
			self.description = result
		self.display_update()
	


class block(ladder.block):
	def __init__(self,function = block_functions.DEFAULT,parent=None,output=False,rung=None):
		super(block,self).__init__(function,parent,output,rung)
		self.display = None
		self.frm2 = None
		self.popupparent = None
		if parent:
			self.display_new(parent)
		if output:
			self.display_update_output()
		else:
			self.display_update()

	def run(self,on=True):
		try:
			super(block,self).run(on)
		except:
			self.display_update()
			raise


	def delifparent(self,parent):
		if self.frm.master == parent:
			#I am a child of you, so I wll delete myself
			if self.frm2:
				self.frm2.destroy()
			for a in self.arguments:
				a.delifparent(self.frm)

	def removeblock(self,child):
		if child in self.arguments:
			self.arguments.remove(child)

	def display_new(self,parent):
		frm = Frame(parent,border=2,relief=RAISED,pady=5)
		frm2 = Frame(frm,border=1,relief=RAISED,)
		frm2.pack(fill=X)
		l = Label(frm2,text=self.function.__name__())
		l.pack()

		for bit in self.arguments:
			bit.display_new(frm)
		frm.pack(side=LEFT)
		self.display = frm

		self.popupparent = parent
		self.popup = Menu(self.popupparent,tearoff=0,postcommand=self.updatemenu)
		addmenu = Menu(self.popup,tearoff=0)
		for item in itemlist:
			addmenu.add_command(label=item.name,command=self.additem(item))
		self.popup.add_cascade(label="add argument",menu=addmenu)
		self.popup.add_command(label="properties",command=self.properties_display)
		self.popup.add_separator()
		self.popup.add_command(label="remove block",command=self.delblock)
		self.popup.add_separator()

		self.updatemenu()
		self.frm = frm
		frm.bind("<Button-3>",self.popup_menu)
		fix_scroll()

	def testing(self):
		print self.arguments
		print self.status()
		print self.function.on

	def properties_display(self):
		ds = self.properties()
		tkMessageBox.showinfo(self.name,ds)

	def updatemenu(self):
		self.popup.delete(5)
		self.popup.delete(4)
		self.popup.delete(3)
		self.popup.delete(2)
		self.popup.delete(1)
		self.popup.delete(1)
		self.popup.delete(1)
		
		addmenu = Menu(self.popup,tearoff=0)
		for item in itemlist:
			addmenu.add_command(label=item.name,command=self.additem(item))
		for fnct in functionlist:
			addmenu.add_command(label=fnct.__name__,command=self.addsubblock(fnct))
		self.popup.add_cascade(label="add argument",menu=addmenu)
		self.popup.add_command(label="properties",command=self.properties)
		self.popup.add_command(label="test",command=self.testing)
		self.popup.add_separator()
		self.popup.add_command(label="remove block",command=self.delblock)
		self.popup.add_separator()



		self.removeargsmenu = Menu(self.popup,tearoff=0)
		for item in self.arguments:
			self.removeargsmenu.add_command(label=item.name,command=self.removearg(item))
		self.popup.add_cascade(label="remove argument",menu=self.removeargsmenu,)
	

	def addsubblock(self,fnct):
		return lambda:self.arguments.append(block(fnct,parent=self.frm,rung=self))
	def removearg(self,item):
		return lambda:self.removearg2(item)
	def removearg2(self,item):
		item.delifparent(self.frm)
		self.arguments.remove(item)

	def additem(self,item):
		return lambda:self.additem2(item)

	def additem2(self,item):
		self.arguments.append(item)
		item.display_new(self.frm)
		fix_scroll()

	def delblock(self):
		if self.frm2:
			self.frm2.destroy()
		self.frm.destroy()


	def popup_menu(self,event):
		try: 
			self.popup.tk_popup(event.x_root,event.y_root,0)
		finally:
			self.popup.grab_release()


	def display_update(self):
		if self.display:
			if self.faulted:
				self.display.configure(bg=FAULTCOLOR)
			elif self.status():
				self.display.configure(bg=TRUECOLOR)
			else:
				self.display.configure(bg=FALSECOLOR)
		for bit in self.arguments:
				bit.display_update()

	def display_update_output(self):
		for bit in self.arguments:
			bit.display_update()


class rung(ladder.rung):
	def __init__(self):
		super(rung,self).__init__()
		self.frminput = None
		self.frmoutput = None
	def __del__(self):
		for i in self.inputs:
			i.delifparent(self.frminput)
		for o in self.outputs:
			o.delifparent(self.frmoutput)

		self.frminput.destroy()
		self.frmoutput.destroy()
		self.frm.destroy()

	def run(self):
		super(rung,self).run()

		conds = map(lambda x:x.status(),self.inputs)
		if all(conds) or not self.inputs:
			if self.frminput:
				self.frminput.configure(bg=TRUECOLOR)
		else:
			if self.frminput:
				self.frminput.configure(bg=FALSECOLOR)
			

	def removeblock(self,child):
		if child in self.inputs:
			self.inputs.remove(child)
		elif child in self.outputs:
			self.outputs.remove(child)

	def display_new(self,parent):
		frm = Frame(parent,border=2,relief=RAISED)
		frmminheight = Frame(frm,height=20,width=1)
		frmminheight.pack(side=LEFT)
		self.frminput = Frame(frm,border=1,relief=RAISED)
		self.frmoutput = Frame(frm,border=1,relief=RAISED)
		self.frminput.pack(side=LEFT,fill=Y)
		self.frmoutput.pack(side=RIGHT,fill=Y)

		for inputblock in self.inputs:
			inputblock.display_new(self.frminput)
		for outputblock in self.outputs:
			outputblock.display_new(self.frmoutput)
		frm.pack(fill=X,expand=1)
		self.display = frm

		self.popup = Menu(parent,tearoff=0)
		self.inputmenu = Menu(self.popup,tearoff=0)
		self.outputmenu = Menu(self.popup,tearoff=0)
		for fnct in functionlist:
			self.inputmenu.add_command(label=fnct.__name__,command=self.addinput(fnct))
			self.outputmenu.add_command(label=fnct.__name__,command=self.addoutput(fnct))
		self.popup.add_cascade(label="add input",menu=self.inputmenu)
		self.popup.add_cascade(label="add output",menu=self.outputmenu)
		self.popup.add_separator()
		self.popup.add_command(label="remove rung",command=self.removerowclick)
		self.popup.add_command(label="new rung")
		self.popup.add_command(label="test",command=self.test)
		frm.bind("<Button-3>",self.popup_menu)
		self.frminput.bind("<Button-3>",self.popup_menu_input)
		fix_scroll()
		self.frm = frm

	def test(self):
		for i in self. inputs:
			i.test(self.frminput)

	def removerowclick(self):
		remove_row(self)

	def addinput(self,fnct):
		return lambda:self.inputs.append(block(fnct,parent=self.frminput,rung=self))

	def addoutput(self,fnct):
		return lambda:self.outputs.append(block(fnct,parent=self.frmoutput,output=True))

	def popup_menu(self,event):
		try: 
			self.popup.tk_popup(event.x_root,event.y_root,0)
		finally:
			self.popup.grab_release()
	def popup_menu_input(self,event):
		try: 
			self.inputmenu.tk_popup(event.x_root,event.y_root,0)
		finally:
			self.popup.grab_release()

	def display_update(self):
		for inputblock in self.inputs:
			inputblock.display_update()
		for outputblock in self.outputs:
			outputblock.display_update_output()



def closeblock(bit):
	b = block()
	b.arguments.append(bit)
	return b


if __name__ == '__main__':
	I1 = create_binary_array(10,'I1:0/')

	b1 = block()
	b1.function = func_and
	b1.arguments.append(I1[0])
	b1.arguments.append(I1[1])
	I1[0].set(True)
	I1[1].set(True)


	a = value_word(0)

	out1 = block()
	out1.function = func_add
	out1.arguments.append(a)
	out1.arguments.append(1)

	out1.run()

	r = rung()
	r.inputs += [b1]
	r.outputs += [out1]

	r.run()

	I1[0].set(False)
	r.run()




