# -*- coding: utf-8 -*-
#-------------------------------------------------
#-- Transform Node: Cascaded coordinate systems 
#--
#-- (c) microelly 2015
#--
#-- GNU Lesser General Public License (LGPL)
#-------------------------------------------------

import FreeCAD,PySide,os,FreeCADGui
from PySide import QtCore, QtGui, QtSvg
from PySide.QtGui import * 

__vers__='0.2'


try:
	__dir__ = os.path.dirname(__file__)
	say(__dir__)
except:
	__dir__='/usr/lib/freecad/Mod/mylib'

def say(s):
	FreeCAD.Console.PrintMessage(str(s)+"\n")

def saye(s):
	FreeCAD.Console.PrintError(str(s)+"\n")


class TransformWidget(QtGui.QWidget):
	def __init__(self, obj,*args):
		QtGui.QWidget.__init__(self, *args)
		self.obj2=obj
		FreeCAD.obj2=obj
		self.vollabel = QtGui.QLabel(obj.Object.Label)

		self.pushButton02 = QtGui.QPushButton()
		self.pushButton02.clicked.connect(self.on_pushButton02_clicked) 
		self.pushButton02.setText("close")

		self.listWidget = QListWidget() 
		for tn in self.obj2.Object._targets:
			n=tn.Label
			item = QListWidgetItem(n)
			self.listWidget.addItem(item)
		self.listWidget.setSelectionMode(QtGui.QAbstractItemView.MultiSelection)

		self.combo = QtGui.QComboBox(self)
		for i in FreeCAD.ActiveDocument.Objects:
			self.combo.addItem(str(i.Label)+ " ")

		self.pushButton03 = QtGui.QPushButton()
		self.pushButton03.clicked.connect(self.on_pushButton03_clicked) 
		self.pushButton03.setText("add target")
		
		self.pushButton04 = QtGui.QPushButton()
		self.pushButton04.clicked.connect(self.on_pushButton04_clicked) 
		self.pushButton04.setText("remove selected targets")
		
		layout = QtGui.QGridLayout()
		layout.addWidget(self.vollabel, 0, 0)
		
		layout.addWidget(self.pushButton02, 15, 0,1,4)
		layout.addWidget(self.listWidget, 3, 0,1,4)
		layout.addWidget(self.pushButton04, 4, 0,1,4)
		
		layout.addWidget(self.combo, 5, 0,1,4)
		layout.addWidget(self.pushButton03, 6, 0,1,4)

		self.setLayout(layout)
		self.setWindowTitle("Edit Coordinate System")

	def on_pushButton02_clicked(self):
		self.hide()

	def on_pushButton03_clicked(self):
		col=self.combo.currentText()
		it=QListWidgetItem(col)
		self.listWidget.addItem(it)
		itemcount =self.listWidget.count()
		newlist=[]
		targets=[]
		for i in range(itemcount):
			y=self.listWidget.item(i).text()
			y=y.strip()
			newlist.append(y)
			l=FreeCAD.ActiveDocument.getObjectsByLabel(y)
			targets.append(l[0])
		self.obj2.Object._targets=targets

	def on_pushButton04_clicked(self):
		seli=[]
		for sel in self.listWidget.selectedItems():
			seli.append(sel.text())
		itemcount =self.listWidget.count()
		newlist=[]
		for i in range(itemcount):
			try:
				y=self.listWidget.item(i).text()
				y=y.strip()
				seli.index(y)
			except:
#				say("nicht gefunden")
				newlist.append(y)
		self.listWidget.clear()
		targets=[]
		for y in newlist:
			self.listWidget.addItem(y)
			l=FreeCAD.ActiveDocument.getObjectsByLabel(y)
			targets.append(l[0])
		self.obj2.Object._targets=targets




#---------------------------------------------
def createTransform(name='MyTransform', targets=[],src=None):
	obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython",name)
	_Transform(obj)
	_ViewProviderTransform(obj.ViewObject)
	
	c3=obj
	c3.addProperty("App::PropertyStringList","execute3","2 MyCTL","")
	c3.execute3=['say("hallo")']
	c3.Proxy.updater=True
	c3.addProperty("App::PropertyStringList","propliste","2 MyCTL","")
	c3.propliste=[]
	c3.addProperty("App::PropertyLinkList","_targets","2 MyCTL","")
	c3._targets=targets
	c3.addProperty("App::PropertyLink","src","2 MyCTL","")
	c3.src=src
	c3.addProperty("App::PropertyPlacement","plOld","2 MyCTL","")
	c3.plOld=FreeCAD.Placement()
	c3.addProperty("App::PropertyPlacement","Placement","Base","")
	if src:
		c3.Placement=src.Placement
	else:
		c3.Placement=FreeCAD.Placement()
	objlinks=[]
	liste=[]
	while objlinks.__len__() > 1: 
		key=objlinks.pop(0)
		val=objlinks.pop(0)
		say(key)
		say(val)
		c3.addProperty("App::PropertyLink",key,"3 MyParts","")
		s='c3.'+key+'=FreeCAD.ActiveDocument.getObject(val)'
		say(s)
		exec(s)
	propliste=[]
	while liste.__len__() > 1: 
		key=liste.pop(0)
		val=liste.pop(0)
		say(key)
		say(val)
		propliste.append(key)
		say(propliste)
		#say(val.__class__)
		if val.__class__ == int:
			c3.addProperty("App::PropertyInteger",key,"1 MyProps","")
			s='c3.'+key + '=' + str(val) 
			exec(s)
		elif val.__class__ ==  str:
			c3.addProperty("App::PropertyString",key,"1 MyProps","")
			s='c3.'+key + '="' + val + '"'
			exec(s)
		elif val.__class__ ==  float:
			c3.addProperty("App::PropertyFloat",key,"1 MyProps","")
			s='c3.'+key + '=' + str(val) 
			#say(s)
			exec(s)
		else:
			say("nicht bearbeiteit")
			say(val.__class__)
	c3.propliste=propliste
	return obj

class _Transform():
	def __init__(self,obj):
		obj.Proxy = self
		self.Type = "_Transform"
		self.obj2 = obj 
		self.Lock=False
	def execute(self,obj):
		if not self.Lock:
			say("exec self=" +str(self) +' obj.Label= ' +str(obj.Label)) 
			say("set Lock ----- " +str(obj.Label))
			'''
			try:
				if not hasattr(self,"updater"):
					say("erzeuge updater")
					self.updater=True
					
				if self.updater:
					self.updater=False
					say("updater true!")
					#return
					# hack deaktiviert 
				else:
					self.updater=True
			except:
				pass
			'''
			self.obj2=obj
			self.Lock=True
			qalt=self.obj2.plOld
			source=self.obj2.src
			if source:
				qneu=source.Placement
			else:
				qneu=self.obj2.Placement
			qai=qalt.inverse()
			self.obj2.plOld=qneu
			for target in self.obj2._targets:
				palt=target.Placement
				t=qai.multiply(palt)
				pneu=qneu.multiply(t) 
				target.Placement=pneu
				try:
					target.Proxy.execute(target)
				except:
					pass
			self.Lock=False
			say("unset Lock +++ " +str(self.obj2.Label))


	def __getstate__(self):
		say("getstate " + str(self))
		return None

	def __setstate__(self,state):
		say("setstate " + str(self) + str(state))
		return None
		
	def addComponent(self,name='object'):
		say(name)
		self.obj2.addProperty("App::PropertyLink",name,"3 MyParts","")
	def addProperty(self,key,val):
		say(key)
		#propliste.append(key)
		#say(propliste)
		say(val.__class__)
		c3=self.obj2
		if val.__class__ == int:
			c3.addProperty("App::PropertyInteger",key,"1 MyProps","")
			s='c3.'+key + '=' + str(val) 
			exec(s)
		elif val.__class__ ==  str:
			c3.addProperty("App::PropertyString",key,"1 MyProps","")
			s='c3.'+key + '="' + val + '"'
			exec(s)
		elif val.__class__ ==  float:
			c3.addProperty("App::PropertyFloat",key,"1 MyProps","")
			s='c3.'+key + '=' + str(val) 
			#say(s)
			exec(s)
		else:
			say("nicht bearbeiteit")
			say(val.__class__)
		say(c3.propliste)
		t=c3.propliste
		t.append(key)
		c3.propliste=t
		say(c3.propliste)

	def onChanged(self,obj,prop):
		if prop=="Placement":
			if str(self.beforeP) != str(obj.Placement):
				say("Placement changed ...")
				say(self.beforeP)
				say(obj.Placement)

	def onBeforeChange(self,obj,prop):
		if prop=="Placement":
			self.beforeP=FreeCAD.Placement(obj.Placement)

class _ViewProviderTransform(object):
 
	def getIcon(self):
		return __dir__ +'/icons/sun.png'
   
	def __init__(self,vobj):
		say("__init__" + str(self))
		self.Object = vobj.Object
		vobj.Proxy = self

	def attach(self,vobj):
		say("attach " + str(vobj.Object.Label))
		self.Object = vobj.Object
		self.obj2=self.Object
		#self.dialog=TransformWidget(self)
		#FreeCAD.t=self
		if not hasattr(self.Object,"Lock"):
				self.Object.Proxy.Lock=False
				say("lock gesetzt")
		FreeCAD.ty=self
		FreeCAD.tv=vobj
		return

	def claimChildren(self):
		return self.Object.Group

	def __getstate__(self):
		say("getstate " + str(self))
		return None

	def __setstate__(self,state):
		say("setstate " + str(self) + str(state))
		return None
		
	def setEdit(self,vobj,mode=0):
		s=TransformWidget(self)
		self.dialog=s
		self.dialog.show()
		say("set Edit")
		#s.show()
		return True

	def unsetEdit(self,vobj,mode=0):
		return False

	def doubleClicked(self,vobj):
		self.setEdit(vobj)

	def setupContextMenu(self, obj, menu):
		action = menu.addAction("About Transform")
		action.triggered.connect(self.showVersion)

		action = menu.addAction("Edit ...")
		action.triggered.connect(self.edit)

	def edit(self):
		self.dialog=TransformWidget(self)
		self.dialog.show()
		

	def showVersion(self):
		QtGui.QMessageBox.information(None, "About Transform", "Transform Node\n2015 microelly\nVersion " + __vers__ +"\nstill very alpha")



#-----------------------

class HingeWidget(QtGui.QWidget):
	def __init__(self, obj,*args):
		QtGui.QWidget.__init__(self, *args)

		self.Object=obj
		layout = QtGui.QGridLayout()
#		self.vollabel = QtGui.QLabel(obj.Object.Label)
#		layout.addWidget(self.vollabel, 0, 0)
		self.dial = QtGui.QDial()
		self.dial.setMaximum(360)
		self.dial.setSingleStep(1)
		self.dial.setNotchesVisible(True)
		self.dial.valueChanged.connect(self.paramValueCanged);
		layout.addWidget(self.dial, 10, 0,1,4)
		self.setLayout(layout)
		self.setWindowTitle("Hinge Joint")

	def paramValueCanged(self):
		say("Angel ValueCanged")
		# say(self.dial.value())
		v=self.dial.value()
		say(v)
		'''
		say(self.Object.Object.Placement.Rotation.Angle)
		say(self.Object.Object.Placement.Rotation.Axis)
		self.Object.Object.Placement.Rotation=FreeCAD.Rotation(self.Object.Object.axis,v)
		saye(self.Object.Object.Placement.Rotation.Angle)
		saye(self.Object.Object.Placement.Rotation.Axis)
		
		say(self.Object.Object.Placement)
		self.Object.Object.Proxy.execute(t)
		'''
		#---------------------------------------------
		
		FreeCAD.tt=self
#		say(self.Object.Object.Placement)
		if True:
			qalt=self.Object.Object.plOld
			qai=qalt.inverse()
#			say(qai)
			vt=self.Object.Object.axisAngle
			say("vt=")
			say(vt)
			qt=FreeCAD.Placement()
			qt.Rotation=FreeCAD.Rotation(self.Object.Object.axis,vt)
#			say(qt)
			vti=qt.inverse()
			# rest=vti.multiply(qalt)
			rest=qalt.multiply(vti)
			say("rest=")
			say(rest)
			qn=FreeCAD.Placement()
			#qn.Base=FreeCAD.Vector(self.Object.Object.direction).multiply(v)
			qn.Rotation=FreeCAD.Rotation(self.Object.Object.axis,v)
			say("neue pos einfach ... qn2=")
			qn2=FreeCAD.Placement(qn)
			rest2=FreeCAD.Placement(rest)
			say(qn2)
			
			say("qneu ... zz=")
			#FreeCAD.rest=rest
			#FreeCAD.qn=qn
			#zz=FreeCAD.rest.multiply(FreeCAD.qn)
			#say(zz)
			say("lokal berechnet:")
			zz=rest.multiply(qn)
			say(zz)
			
			self.Object.Object.axisAngle=v
			self.Object.Object.Placement=zz
			saye("fertig")
			FreeCAD.ActiveDocument.recompute()
		
		
		#----------------------------------------------
		FreeCAD.ActiveDocument.recompute()


class TelescopWidget(QtGui.QWidget):
	def __init__(self, obj,*args):
		QtGui.QWidget.__init__(self, *args)
		self.Object=obj
		layout = QtGui.QGridLayout()
#		self.vollabel = QtGui.QLabel("Telescopic boom")
#		layout.addWidget(self.vollabel, 0, 0)
		self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
		self.slider.setMaximum(100)
		self.slider.valueChanged.connect(self.paramValueCanged);
		layout.addWidget(self.slider, 12, 0,1,4)
		self.setLayout(layout)
		self.setWindowTitle("Telecopic Boom")

	def paramValueCanged(self):
		saye("ValueCanged")
		say(self.slider.value())
		v=self.slider.value()
		FreeCAD.tt=self
#		say(self.Object.Object.Placement)
		if True:
			qalt=self.Object.Object.plOld
			qai=qalt.inverse()
#			say(qai)
			vt=self.Object.Object.axisScale
#			say("vt=")
#			say(vt)
			qt=FreeCAD.Placement()
			qt.Base=FreeCAD.Vector(self.Object.Object.direction).multiply(vt)
#			say(qt)
			vti=qt.inverse()
			# rest=vti.multiply(qalt)
			rest=qalt.multiply(vti)
			say("rest=")
			say(rest)
			qn=FreeCAD.Placement()
			qn.Base=FreeCAD.Vector(self.Object.Object.direction).multiply(v)
			say("neue pos einfach ... qn2=")
			qn2=FreeCAD.Placement(qn)
			rest2=FreeCAD.Placement(rest)
			say(qn2)
			
			say("qneu ... zz=")
			#FreeCAD.rest=rest
			#FreeCAD.qn=qn
			#zz=FreeCAD.rest.multiply(FreeCAD.qn)
			#say(zz)
			say("lokal berechnet:")
			zz=rest.multiply(qn)
			say(zz)
			
			self.Object.Object.axisScale=v
			self.Object.Object.Placement=zz
			saye("fertig")
			FreeCAD.ActiveDocument.recompute()


class _ViewProviderHinge(_ViewProviderTransform):
	def setupContextMenu(self, obj, menu):
#		action = menu.addAction("About Transform ")
#		action.triggered.connect(self.showVersion)
		action = menu.addAction("Edit ... ")
		action.triggered.connect(self.edit)
		action = menu.addAction("Config ... ")
		action.triggered.connect(self.config)

	def config(self):
		self.dialog=HingeWidget(self)
		# self.dialog.show()
		
		mw = FreeCADGui.getMainWindow()
		dock = QtGui.QDockWidget(self.Object.Label, mw)
		dock.setStyleSheet("background-color:lightblue;color:blue;")
		mw.centralWidget = QtGui.QWidget(dock)
		mw.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dock)
		dock.setWidget(self.dialog)
		say("widget set")


	def doubleClicked(self,vobj):
		self.setEdit(vobj)

	def setEdit(self,vobj,mode=0):
		self.config()
		saye("edit ..")
		return True


class _ViewProviderTelescope(_ViewProviderTransform):

	def getIcon(self):
		return __dir__ +'/icons/mars.png'
 
	def setupContextMenu(self, obj, menu):
#		action = menu.addAction("About Transform B")
#		action.triggered.connect(self.showVersion)
		action = menu.addAction("Edit ... ")
		action.triggered.connect(self.edit)
		action = menu.addAction("Config ... ")
		action.triggered.connect(self.config)

	def config(self):
		self.dialog=TelescopWidget(self)
		#self.dialog.show()
		mw = FreeCADGui.getMainWindow()
		#dock = QtGui.QDockWidget("Mein Dock", mw)
		dock = QtGui.QDockWidget(self.Object.Label, mw)
		dock.setStyleSheet("background-color:yellow;color:brown;")
		mw.centralWidget = QtGui.QWidget(dock)
		mw.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dock)
		dock.setWidget(self.dialog)

	def doubleClicked(self,vobj):
		self.setEdit(vobj)

	def setEdit(self,vobj,mode=0):
		self.config()
		saye("edit ..")
		return True


def createHinge(name='MyHinge', targets=[],src=None):
	obj=createTransform(name, targets=[],src=None)
	obj.addProperty("App::PropertyVector","axis","2 MyCTL","")
	obj.axis=FreeCAD.Vector(0,0,1)
	obj.addProperty("App::PropertyFloat","axisAngle","2 MyCTL","")
	obj.axisAngle=0.0
	_ViewProviderHinge(obj.ViewObject)
	return obj

def createTelescope(name='MyTelescope', targets=[],src=None):
	obj=createTransform(name, targets=[],src=None)
	obj.addProperty("App::PropertyVector","direction","2 MyCTL","")
	obj.direction=FreeCAD.Vector(0,0,1)
	obj.addProperty("App::PropertyFloat","axisScale","2 MyCTL","")
	obj.axisScale=0.0

	_ViewProviderTelescope(obj.ViewObject)
	return obj

#---------------------------------

# vertexPlugger

App=FreeCAD

def createVertexPlugger(name='VertexPlugger', src=None, target=None, point=0):
	obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython",name)
	
	c3=obj
	

	#c3.addProperty("App::PropertyLinkList","_targets","3 MyCTL","")
	#c3._targets=targets
	
	c3.addProperty("App::PropertyLink","target","2 MyCTL","")
	c3.target=target
	c3.addProperty("App::PropertyLink","src","2 MyCTL","")
	c3.src=src
	c3.addProperty("App::PropertyInteger","point","2 MyCTL","")
	c3.point=point
	c3.addProperty("App::PropertyPlacement","plOld","2 MyCTL","")
	c3.plOld=FreeCAD.Placement()
	if src:
		c3.plOld=src.Placement
	c3.addProperty("App::PropertyVector","refOld","2 MyCTL","")
	c3.refOld=FreeCAD.Vector()
	if src:
		c3.refOld=FreeCAD.Vector(src.Shape.Vertexes[point].Point)

	for p in ['point','src','target','plOld','refOld']:
		obj.setEditorMode(p, 1) #ro
	_VertexPlugger(obj)
	_ViewProviderVertexPlugger(obj.ViewObject)
	c3.Proxy.updater=True
	return obj

class _VertexPlugger():

	def __init__(self,obj):
		obj.Proxy = self
		self.Type = "_Transform"
		self.obj2 = obj 
		self.Lock=False
		self.Changed=False

	def execute(self,obj):
		if self.Changed:
			say("self changed")
			# ignore self changes
			self.Changed=False
			return
		if not self.Lock:
#			say("exec self=" +str(self))
			
			say("set Lock ----- " +str(obj.Label))
			self.obj2=obj

			self.Lock=True
			try:
				self.update()
			except:
				say("fehler update")
				pass
			self.Lock=False
			say("unset Lock +++ " +str(self.obj2.Label))

	def update(self):
		lastrefalt=FreeCAD.Placement()
		lastrefalt.Base=self.obj2.refOld
		
		koopalt=FreeCAD.Placement(self.obj2.src.Placement)
		refalt=FreeCAD.Vector(self.obj2.src.Shape.Vertexes[self.obj2.point].Point)
		
		refalt2=FreeCAD.Placement()
		refalt2.Base=refalt

		oldobinv=self.obj2.plOld.inverse()
		oldrelref=oldobinv.multiply(lastrefalt)
		oldrelrefinv=oldrelref.inverse()
		
		s1=oldobinv.multiply(self.obj2.target.Placement)
		s2=oldrelrefinv.multiply(s1)
		s3=refalt2.multiply(s2)
		self.obj2.target.Placement=s3
		
		self.obj2.plOld=koopalt
		self.obj2.refOld=refalt



	def __getstate__(self):
		say("getstate " + str(self))
		return None

	def __setstate__(self,state):
		say("setstate " + str(self) + str(state))
		return None

	def onChanged(self,obj,prop):
		pass
#		say("on Changed")
#		say(obj)
#		say(prop)

	def onBeforeChange(self,obj,prop):
		pass
#		say("on before change")

class _ViewProviderVertexPlugger(object):
 
	def getIcon(self):
		return __dir__ +'/icons/sun.png'
   
	def __init__(self,vobj):
		say("__init__" + str(self))
		self.Object = vobj.Object
		vobj.Proxy = self

	def attach(self,vobj):
		say("attach " + str(vobj.Object.Label))
		self.Object = vobj.Object
		self.obj2=self.Object
		#self.dialog=TransformWidget(self)
		#FreeCAD.t=self
		if not hasattr(self.Object,"Lock"):
				self.Object.Proxy.Lock=False
				say("lock gesetzt")
		self.Object.Proxy.Lock=False
		self.Object.Proxy.Changed=False
		return

	def claimChildren(self):
		return self.Object.Group

	def __getstate__(self):
		say("getstate " + str(self))
		return None

	def __setstate__(self,state):
		say("setstate " + str(self) + str(state))
		return None
		
	def setEdit(self,vobj,mode=0):
		##s=TransformWidget(self)
		say("still no editor")
		return
		# noc zu impl
		s=VertexPluggerWidget(self)
		self.dialog=s
		self.dialog.show()
		say("set Edit")
		return True

	def unsetEdit(self,vobj,mode=0):
		return False

	def doubleClicked(self,vobj):
		self.setEdit(vobj)

	def setupContextMenu(self, obj, menu):
		action = menu.addAction("About VertexPlugger")
		action.triggered.connect(self.showVersion)

		action = menu.addAction("Edit ...")
		action.triggered.connect(self.edit)

	def edit(self):
		self.dialog=VertexPluggerWidget(self)
		self.dialog.show()

	def showVersion(self):
		QtGui.QMessageBox.information(None, "About VertexPlugger", "Vertex Plugger Node\n2015 microelly\nVersion " + __vers__ +"\nstill very alpha")