"""
CopyCenterPlugin to provide 'TQtSQL'.

Description:
This python-script is a plugin for the CopyCenter.py.

Author:
Sebastian Sauer <mail@dipe.org>

Copyright:
GPL v2 or higher.
"""

class CopyCenterPlugin:
	""" The CopyCenterPlugin to provide 'TQtSQL' to CopyCenter.py """

	name = "TQtSQL Database"
	""" The name this plugin has. The name should be unique and
	will be used for displaying a caption. """

	class Plugin:
		def _init_(self,copycenterplugin):
			self.copycenterplugin = copycenterplugin
			self.widget = None
			self.database = None
			self.cursor = None
			self.isfinished = True
		def _init(self,copierer):
			self.copierer = copierer
			if not self.widget.connectClicked():
				raise Exception("Failed to connect with database.")
			if self.database == None or not self.database.isOpen():
				raise Exception("Database is not initialized or not opened.")
			self.copierer.appendProgressMessage("Connected: %s %s@%s:%i %s" %
				(str(self.database.driverName()),str(self.database.userName()),str(self.database.hostName()),self.database.port(),str(self.database.databaseName())) )
			self.isfinished = False
		def isFinished(self):
			return self.isfinished
		def finish(self):
			self.isfinished = True
			self.widget.disconnectClicked()
		def createWidget(self,dialog,parent):
			return self.copycenterplugin.widget(dialog, self, parent)

	class Source(Plugin):
		plugintype = "Source"
		def __init__(self,copycenterplugin):
			self._init_(copycenterplugin)
			self.options = {
				'driver': 'TQMYSQL3', #'TQMYSQL3','TQPSQL7','TQODBC3',...
				'hostname': '127.0.0.1',
				'port': 3306,
				'username': 'root', #'MyUsername',
				'password': '', #'MySecretPassword',
				'database': '', #'MyTQtSQLDatabase',
				'table': '', #'table1',
				'fields': '', #'f1,f2',
				'where': '',
			}
		def init(self,copierer):
			self._init(copierer)
			tablename = str(self.widget.tableedit.text())
			wherestatement = str(self.widget.whereedit.text())
			from TQt import tqt
			from TQt import tqtsql
			self.cursor = tqtsql.TQSqlCursor(tablename,True,self.database)
			self.cursor.setFilter(wherestatement)
			if not self.cursor.select():
				raise Exception("Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) ))
			self.fieldlist = []
			for fieldname in str(self.widget.fieldedit.text()).split(","):
				fn = fieldname.strip()
				if fn != "":
					field = self.cursor.field(fn)
					if not field:
						raise Exception("There exists no such field \"%s\" in the table \"%s\"." % (fn,tablename))
					self.fieldlist.append(str(field.name()))
			if len(self.fieldlist) < 1:
				raise Exception("No fields for table \"%s\" defined." % tablename)
			copierer.appendProgressMessage("SQL: %s" % str(self.cursor.executedQuery()))

		def read(self):
			if not next(self.cursor):
				return None
			record = []
			for fieldname in self.fieldlist:
				record.append( str(self.cursor.value(fieldname).toString()).encode("latin-1") )
			#print "read record: %s" % record
			return record

	class Destination(Plugin):
		plugintype = "Destination"
		def __init__(self,copycenterplugin):
			self._init_(copycenterplugin)
			self.options = {
				'driver': 'TQMYSQL3', #'TQMYSQL3','TQPSQL7','TQODBC3',...
				'hostname': '127.0.0.1',
				'port': 3306,
				'username': 'root', #'MyUsername',
				'password': '', #'MySecretPassword',
				'database': '', #'MyTQtSQLDatabase',
				'table': '', #'table2',
				'fields': '', #'field1,field2',
				'operation': 'Insert', #'Insert','Update'...
				'indexfield': '',
			}
		def init(self,copierer):
			self._init(copierer)
			from TQt import tqt
			from TQt import tqtsql

			self.fieldlist = []
			for fieldname in str(self.widget.fieldedit.text()).split(","):
				fn = fieldname.strip()
				if fn != "": self.fieldlist.append(fn)

			tablename = str(self.widget.tableedit.text())
			self.cursor = tqtsql.TQSqlCursor(tablename,True,self.database)
			{
				0: self.initInsert,
				1: self.initUpdate
			}[ self.widget.operationedit.currentItem() ]()

		def initInsert(self):
			self.write = self.writeInsert
			if not self.cursor.select():
				raise Exception("Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) ))
			for fieldname in self.fieldlist: # check fieldlist
				field = self.cursor.field(fieldname)
				if not field: raise Exception("There exists no such field \"%s\" in the table \"%s\"." % (fieldname, self.cursor.name()))
			self.copierer.appendProgressMessage("Insert SQL: %s" % str(self.cursor.executedQuery()))

		def writeInsert(self, record):
			print("insert record: %s" % record)
			from TQt import tqt
			cursorrecord = self.cursor.primeInsert()
			count = len(record)
			for i in range(len(self.fieldlist)):
				if i == count: break
				r = record[i]
				if r == None:
					v = tqt.TQVariant()
				else:
					v = tqt.TQVariant(r)
				cursorrecord.setValue(self.fieldlist[i], v)
			rowcount = self.cursor.insert()
			if rowcount < 1:
				drv = str(self.cursor.lastError().driverText()).encode("latin-1")
				db = str(self.cursor.lastError().databaseText()).encode("latin-1")
				print("failed: %s %s" % (drv,db))
				self.copierer.writeFailed(record)
			else:
				self.copierer.writeSuccess(record,rowcount)
			#import time
			#time.sleep(1)
			return True

		def initUpdate(self):
			self.write = self.writeUpdate
			self.indexfieldname = str(self.widget.indexedit.text()).strip()
			if self.indexfieldname == "": raise Exception("No index-field defined.")
			pkindex = self.cursor.index(self.indexfieldname)
			if not pkindex: raise Exception("Invalid index-field defined.")
			self.cursor.setPrimaryIndex(pkindex)
			#self.cursor.setMode( tqtsql.TQSqlCursor.Insert | tqtsql.TQSqlCursor.Update )
			self.copierer.appendProgressMessage("Update SQL: %s" % str(self.cursor.executedQuery()))

		def writeUpdate(self, record):
			from TQt import tqt
			# determinate the primary-index
			try:
				idx = self.fieldlist.index(self.indexfieldname)
				indexvalue = record[idx]
			except:
				import traceback
				print("".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ))
				raise Exception("Failed to determinate the value for the primary key.")
			# select cursor and go to matching record.
			wherestatement = "%s = \"%s\"" % (self.indexfieldname, indexvalue)
			if not self.cursor.select(wherestatement):
				raise Exception("Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) ))
			if not next(self.cursor):
				#print "No such record to update !"
				return False
			# Prepare updating the record.
			cursorrecord = self.cursor.primeUpdate()
			# Update the fields in the record.
			count = len(record)
			for i in range(len(self.fieldlist)):
				if i == count: break
				fieldname = self.fieldlist[i]
				if self.indexfieldname != fieldname: # don't update the indexfield!
					r = record[i]
					if r == None:
						v = tqt.TQVariant()
					else:
						v = tqt.TQVariant(r)
					cursorrecord.setValue(fieldname, v)
			# Write updated record.
			rowcount = self.cursor.update()
			if rowcount < 1:
				self.copierer.writeFailed(record)
			else:
				self.copierer.writeSuccess(record,rowcount)
				print("updated record (rowcount %s): %s" % (rowcount,record))
			return True

	def __init__(self, copycenter):
		""" Constructor. """
		pass

	def widget(self,dialog,plugin,parent):
		""" Each plugin may provide a tqt.TQWidget back to the
		CopyCenter.py. The widget will be used to configure our
		plugin settings. """

		from TQt import tqt
		import os

		self.dialog = dialog
		ListViewDialog = self.dialog.ListViewDialog
		class TableDialog(ListViewDialog):
			def __init__(self, mainwidget):
				ListViewDialog.__init__(self,mainwidget,"Tables")
				self.mainwidget = mainwidget
				self.listview.addColumn("Name")
				text = str(self.mainwidget.tableedit.text())
				item = None
				for table in self.mainwidget.plugin.database.tables():
					if item == None:
						item = tqt.TQListViewItem(self.listview,table)
					else:
						item = tqt.TQListViewItem(self.listview,item,table)
					if table == text:
						self.listview.setSelected(item,True)
						self.listview.ensureItemVisible(item)
				tqt.TQObject.connect(self.listview, tqt.TQ_SIGNAL("doubleClicked(TQListViewItem*, const TQPoint&, int)"), self.okClicked)
				tqt.TQObject.connect(self.okbtn, tqt.TQ_SIGNAL("clicked()"), self.okClicked)
			def okClicked(self):
				item = self.listview.selectedItem()
				if item == None:
					self.mainwidget.tableedit.setText("")
				else:
					self.mainwidget.tableedit.setText(item.text(0))
				self.close()

		class FieldsDialog(ListViewDialog):
			def __init__(self, mainwidget):
				ListViewDialog.__init__(self,parent,"Fields")
				self.mainwidget = mainwidget
				self.listview.setSelectionMode(tqt.TQListView.Multi)
				self.listview.setSorting(-1)
				self.listview.header().setClickEnabled(False)
				self.listview.addColumn("Name")
				self.listview.addColumn("Type")
				self.listview.addColumn("Options")
				tablename = str(self.mainwidget.tableedit.text())
				recinfo = self.mainwidget.plugin.database.recordInfo(tablename)
				if recinfo != None:
					fieldslist = str(self.mainwidget.fieldedit.text()).split(",")
					allfields = ("*" in fieldslist)
					item = None
					for fieldinfo in recinfo:
						opts = ""
						for s in ('Required','Calculated'): #,'Generated'):
							if getattr(fieldinfo,"is%s" % s)(): opts += "%s " % s
						item = self.addItem((fieldinfo.name(), tqt.TQVariant.typeToName(fieldinfo.type()), opts),item)
						if allfields or fieldinfo.name() in fieldslist:
							self.listview.setSelected(item,True)
				tqt.TQObject.connect(self.okbtn, tqt.TQ_SIGNAL("clicked()"), self.okClicked)
			def okClicked(self):
				selitems = []
				item = self.listview.firstChild()
				while item:
					if item.isSelected():
						selitems.append(str(item.text(0)))
					item = item.nextSibling()
				self.mainwidget.fieldedit.setText(",".join(selitems))
				self.close()


		class MainWidget(tqt.TQHBox):
			def __init__(self,plugin,dialog,parent):
				from TQt import tqt
				from TQt import tqtsql
				tqt.TQHBox.__init__(self,parent)
				self.dialog = dialog
				self.plugin = plugin

				self.connectionbox = tqt.TQVBox(parent)
				self.connectionbox.setSpacing(2)

				driverbox = tqt.TQHBox(self.connectionbox)
				driverlabel = tqt.TQLabel("Driver:",driverbox)
				self.driveredit = tqt.TQComboBox(driverbox)
				for driver in tqtsql.TQSqlDatabase.drivers():
					self.driveredit.insertItem(driver)
					if self.plugin.options['driver'] == driver:
						self.driveredit.setCurrentItem(self.driveredit.count() - 1)
				driverlabel.setBuddy(self.driveredit)
				driverbox.setStretchFactor(self.driveredit,1)

				hostbox = tqt.TQHBox(self.connectionbox)
				hostlabel = tqt.TQLabel("Hostname:",hostbox)
				self.hostedit = tqt.TQLineEdit(self.plugin.options['hostname'],hostbox)
				hostlabel.setBuddy(self.hostedit)
				hostbox.setStretchFactor(self.hostedit,1)

				portbox = tqt.TQHBox(self.connectionbox)
				portlabel = tqt.TQLabel("Port:",portbox)
				self.portedit = tqt.TQLineEdit(str(self.plugin.options['port']),portbox)
				portlabel.setBuddy(self.portedit)
				portbox.setStretchFactor(self.portedit,1)

				userbox = tqt.TQHBox(self.connectionbox)
				userlabel = tqt.TQLabel("Username:",userbox)
				self.useredit = tqt.TQLineEdit(self.plugin.options['username'],userbox)
				userlabel.setBuddy(self.useredit)
				userbox.setStretchFactor(self.useredit,1)

				passbox = tqt.TQHBox(self.connectionbox)
				passlabel = tqt.TQLabel("Password:",passbox)
				self.passedit = tqt.TQLineEdit(self.plugin.options['password'],passbox)
				self.passedit.setEchoMode(tqt.TQLineEdit.Password)
				passlabel.setBuddy(self.passedit)
				passbox.setStretchFactor(self.passedit,1)

				dbbox = tqt.TQHBox(self.connectionbox)
				dblabel = tqt.TQLabel("Database:",dbbox)
				self.dbedit = tqt.TQLineEdit(self.plugin.options['database'],dbbox)
				dblabel.setBuddy(self.dbedit)
				dbbox.setStretchFactor(self.dbedit,1)

				statusbar = tqt.TQHBox(parent)
				statusbar.setSpacing(2)
				statusbar.setStretchFactor(tqt.TQWidget(statusbar),1)
				self.connectbtn = tqt.TQPushButton("Connect",statusbar)
				tqt.TQObject.connect(self.connectbtn, tqt.TQ_SIGNAL("clicked()"),self.connectClicked)
				self.disconnectbtn = tqt.TQPushButton("Disconnect",statusbar)
				self.disconnectbtn.setEnabled(False)
				tqt.TQObject.connect(self.disconnectbtn, tqt.TQ_SIGNAL("clicked()"),self.disconnectClicked)

				tablebox = tqt.TQHBox(parent)
				tablelabel = tqt.TQLabel("Table:",tablebox)
				self.tableedit = tqt.TQLineEdit(self.plugin.options['table'],tablebox)
				tqt.TQObject.connect(self.tableedit, tqt.TQ_SIGNAL("textChanged(const TQString&)"), self.tableEditChanged)
				self.tablebtn = tqt.TQPushButton("...",tablebox)
				self.tablebtn.setEnabled(False)
				tqt.TQObject.connect(self.tablebtn, tqt.TQ_SIGNAL("clicked()"), self.tableBtnClicked)
				tablelabel.setBuddy(self.tableedit)
				tablebox.setStretchFactor(self.tableedit,1)

				fieldbox = tqt.TQHBox(parent)
				fieldlabel = tqt.TQLabel("Fields:",fieldbox)
				self.fieldedit = tqt.TQLineEdit(self.plugin.options['fields'],fieldbox)
				self.fieldbtn = tqt.TQPushButton("...",fieldbox)
				self.fieldbtn.setEnabled(False)
				tqt.TQObject.connect(self.fieldbtn, tqt.TQ_SIGNAL("clicked()"), self.fieldBtnClicked)
				fieldlabel.setBuddy(self.fieldedit)
				fieldbox.setStretchFactor(self.fieldedit,1)

				if self.plugin.plugintype == "Source":
					box = tqt.TQHBox(parent)
					wherelabel = tqt.TQLabel("Where:",box)
					self.whereedit = tqt.TQLineEdit(self.plugin.options['where'],box)
					wherelabel.setBuddy(self.whereedit)
					box.setStretchFactor(self.whereedit,1)
				elif self.plugin.plugintype == "Destination":

					class OperationBox(tqt.TQVBox):
						def __init__(self, mainwidget, parent):
							self.mainwidget = mainwidget
							tqt.TQVBox.__init__(self, parent)
							opbox = tqt.TQHBox(self)
							operationlabel = tqt.TQLabel("Operation:",opbox)
							self.mainwidget.operationedit = tqt.TQComboBox(opbox)
							for op in ('Insert','Update'):
								self.mainwidget.operationedit.insertItem(op)
								if self.mainwidget.plugin.options['operation'] == op:
									self.mainwidget.operationedit.setCurrentItem(self.mainwidget.operationedit.count() - 1)
							operationlabel.setBuddy(self.mainwidget.operationedit)
							opbox.setStretchFactor(self.mainwidget.operationedit,1)
							self.box = None
							tqt.TQObject.connect(self.mainwidget.operationedit, tqt.TQ_SIGNAL("activated(int)"), self.operationActivated)
							self.operationActivated()
						def operationActivated(self, **args):
							if self.box:
								self.box.hide()
								self.box.destroy()
								self.box = None
							def showInsert(self):
								pass
							def showUpdate(self):
								self.box = tqt.TQHBox(self)
								indexlabel = tqt.TQLabel("Indexfield:", self.box)
								self.mainwidget.indexedit = tqt.TQLineEdit(self.mainwidget.plugin.options['indexfield'], self.box)
								indexlabel.setBuddy(self.mainwidget.indexedit)
								self.box.setStretchFactor(self.mainwidget.indexedit,1)
							{
								0: showInsert,
								1: showUpdate,
							}[ self.mainwidget.operationedit.currentItem() ](self)
							if self.box != None: self.box.show()
					OperationBox(self,parent)

			def tableEditChanged(self,text):
				if self.plugin.database != None and self.plugin.database.isOpen():
					if str(text) in self.plugin.database.tables():
						self.fieldbtn.setEnabled(True)
						return
				self.fieldbtn.setEnabled(False)

			def tableBtnClicked(self):
				dialog = TableDialog(self)
				dialog.show()

			def fieldBtnClicked(self):
				dialog = FieldsDialog(self)
				dialog.show()

			def updateConnectState(self):
				connected = self.plugin.database != None and self.plugin.database.isOpen()
				self.connectionbox.setEnabled(not connected)
				self.connectbtn.setEnabled(not connected)
				self.disconnectbtn.setEnabled(connected)
				self.tablebtn.setEnabled(connected)
				self.tableEditChanged(self.tableedit.text())

			def getOptionValue(self,optionname):
				try:
					if optionname == 'driver': return str(self.driveredit.currentText())
					if optionname == 'hostname': return str(self.hostedit.text())
					if optionname == 'port': return str(self.portedit.text())
					if optionname == 'username': return str(self.useredit.text())
					if optionname == 'password': return str(self.passedit.text())
					if optionname == 'database': return str(self.dbedit.text())
					if optionname == 'table': return str(self.tableedit.text())
					if optionname == 'fields': return str(self.fieldedit.text())
					if optionname == 'where': return str(self.whereedit.text())
					if optionname == 'operation': return str(self.operationedit.currentText())
					if optionname == 'indexfield': return str(self.indexedit.text())
				except:
					import traceback
					print("".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ))
				return ""

			def connectClicked(self):
				if self.plugin.database != None and self.plugin.database.isOpen():
					print("already connected. not needed to reconnect...")
					self.updateConnectState()
					return True
				print("trying to connect...")

				from TQt import tqtsql
				drivername = str(self.driveredit.currentText())
				print("drivername: %s" % drivername)
				connectionname = "CopyCenter%s" % self.plugin.plugintype
				print("connectionname: %s" % connectionname)
				self.plugin.database = tqtsql.TQSqlDatabase.addDatabase(drivername,connectionname)
				if not self.plugin.database:
					tqt.TQMessageBox.critical(self,"Failed to connect","<qt>Failed to create database for driver \"%s\"</qt>" % drivername)
					return False

				hostname = str(self.hostedit.text())
				self.plugin.database.setHostName(hostname)

				portnumber = int(str(self.portedit.text()))
				self.plugin.database.setPort(portnumber)

				username = str(self.useredit.text())
				self.plugin.database.setUserName(username)

				password = str(self.passedit.text())
				self.plugin.database.setPassword(password)

				databasename = str(self.dbedit.text())
				self.plugin.database.setDatabaseName(databasename)

				if not self.plugin.database.open():
					tqt.TQMessageBox.critical(self,"Failed to connect","<qt>%s<br><br>%s</qt>" % (self.plugin.database.lastError().driverText(),self.plugin.database.lastError().databaseText()))
					return False
				print("database is opened now!")
				self.updateConnectState()
				return True

			def disconnectClicked(self):
				print("trying to disconnect...")
				if self.plugin.database:
					self.plugin.database.close()
					self.plugin.database = None
				print("database is closed now!")
				self.updateConnectState()

		plugin.widget = MainWidget(plugin,self.dialog,parent)
		return plugin.widget
