# -*- coding: utf-8 -*-

"""
/***************************************************************************
Name                 : DB Manager
Description          : Database manager plugin for QuantumGIS
Date                 : May 23, 2011
copyright            : (C) 2011 by Giuseppe Sucameli
email                : brush.tyler@gmail.com

The content of this file is based on
- PG_Manager by Martin Dobias (GPLv2 license)
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from .info_viewer import InfoViewer
from .table_viewer import TableViewer
from .layer_preview import LayerPreview

from .db_tree import DBTree

from .db_plugins import getDbPluginErrors
from .db_plugins.plugin import BaseError
from .dlg_db_error import DlgDbError


class DBManager(QMainWindow):

	def __init__(self, iface, parent=None):
		QMainWindow.__init__(self, parent)
		self.setAttribute(Qt.WA_DeleteOnClose)
		self.setupUi()
		self.iface = iface

		# restore the window state
		settings = QSettings()
		self.restoreGeometry( settings.value("/DB_Manager/mainWindow/geometry").toByteArray() )
		self.restoreState( settings.value("/DB_Manager/mainWindow/windowState").toByteArray() )

		self.connect(self.tabs, SIGNAL("currentChanged(int)"), self.tabChanged)
		self.connect(self.tree, SIGNAL("selectedItemChanged"), self.itemChanged)
		self.itemChanged(None)

		self.displayDbPluginErrors()

	def displayDbPluginErrors(self):
		if len(getDbPluginErrors()) <= 0:
			return

		if not hasattr(self, '_dbPluginErrorIndex') or self._dbPluginErrorIndex >= len(getDbPluginErrors()):
			self._dbPluginErrorIndex = 0

		msg = getDbPluginErrors()[self._dbPluginErrorIndex]
		self._dbPluginErrorIndex += 1

		self.statusBar.showMessage( msg, 5000 )
		QTimer.singleShot( 6000, self.displayDbPluginErrors)


	def closeEvent(self, e):
		self.unregisterAllActions()

		# save the window state
		settings = QSettings()
		settings.setValue( "/DB_Manager/mainWindow/windowState", QVariant(self.saveState()) )
		settings.setValue( "/DB_Manager/mainWindow/geometry", QVariant(self.saveGeometry()) )

		QMainWindow.closeEvent(self, e)


	def refreshItem(self, item=None):
		QApplication.setOverrideCursor(Qt.WaitCursor)
		try:
			if item == None:
				item = self.tree.currentItem()
			self.tree.refreshItem(item)	# refresh item children in the db tree
		except BaseError, e:
			DlgDbError.showError(e, self)
			return
		finally:
			QApplication.restoreOverrideCursor()

	def itemChanged(self, item):
		QApplication.setOverrideCursor(Qt.WaitCursor)
		try:
			self.reloadButtons()
			self.refreshTabs()
		except BaseError, e:
			DlgDbError.showError(e, self)
			return
		finally:
			QApplication.restoreOverrideCursor()


	def reloadButtons(self):
		db = self.tree.currentDatabase()
		if not hasattr(self, '_lastDb'):
			self._lastDb = db

		elif db == self._lastDb:
			return

		# remove old actions
		if self._lastDb != None:
			self.unregisterAllActions()

		# add actions of the selected database
		self._lastDb = db
		if self._lastDb != None:
			self._lastDb.registerAllActions(self)


	def tabChanged(self, index):
		QApplication.setOverrideCursor(Qt.WaitCursor)
		try:
			self.refreshTabs()
		except BaseError, e:
			DlgDbError.showError(e, self)
			return
		finally:
			QApplication.restoreOverrideCursor()


	def refreshTabs(self):
		index = self.tabs.currentIndex()
		item  = self.tree.currentItem()
		table  = self.tree.currentTable()

		# enable/disable tabs
		self.tabs.setTabEnabled( self.tabs.indexOf(self.table), table != None )
		self.tabs.setTabEnabled( self.tabs.indexOf(self.preview), table != None and table.type in [table.VectorType, table.RasterType] and table.geomColumn != None )
		# show the info tab if the current tab is disabled
		if not self.tabs.isTabEnabled( index ):
			self.tabs.setCurrentWidget( self.info )

		current_tab = self.tabs.currentWidget()
		if current_tab == self.info:
			self.info.showInfo( item )
		elif current_tab == self.table:
			self.table.loadData( item )
		elif current_tab == self.preview:
			self.preview.loadPreview( item )


	def refreshActionSlot(self):
		self.info.setDirty()
		self.table.setDirty()
		self.preview.setDirty()
		self.refreshItem()

	def runSqlWindow(self):
		db = self.tree.currentDatabase()
		if db == None:
			QMessageBox.information(self, "Sorry", "No database selected or you are not connected to it.")
			return

		from dlg_sql_window import DlgSqlWindow
		dlg = DlgSqlWindow(self.iface, db, self)
		#refreshDb = lambda x: self.refreshItem( db.connection() ) # refresh the database tree
		#self.connect( dlg, SIGNAL( "queryExecuted(const QString &)" ), refreshDb )
		dlg.show()
		dlg.exec_()
		

	def showSystemTables(self):
		self.tree.showSystemTables( self.actionShowSystemTables.isChecked() )


	def registerAction(self, action, menuName, callback=None):
		""" register an action to the manager's main menu """
		if not hasattr(self, '_registeredDbActions'):
			self._registeredDbActions = {}

		if callback != None:
			invoke_callback = lambda x: self.invokeCallback( callback )

		if menuName == None or menuName == "":
			self.addAction( action )

			if not self._registeredDbActions.has_key(menuName):
				self._registeredDbActions[menuName] = list()
			self._registeredDbActions[menuName].append(action)

			if callback != None:
				QObject.connect( action, SIGNAL("triggered(bool)"), invoke_callback )
			return True

		# search for the menu
		actionMenu = None
		helpMenuAction = None
		for a in self.menuBar.actions():
			if not a.menu() or a.menu().title() != menuName:
				continue
			if a.menu() != self.menuHelp:
				helpMenuAction = a

			actionMenu = a
			break

		# not found, add a new menu before the help menu
		if actionMenu == None:
			menu = QMenu(menuName, self)
			if helpMenuAction != None:
				actionMenu = self.menuBar.insertMenu(helpMenuAction, menu)
			else:
				actionMenu = self.menuBar.addMenu(menu)

		menu = actionMenu.menu()
		menuActions = menu.actions()

		# get the placeholder's position to insert before it
		pos = 0
		for pos in range(len(menuActions)):
			if menuActions[pos].isSeparator() and menuActions[pos].objectName().endsWith("_placeholder"):
				menuActions[pos].setVisible(True)
				break

		if pos < len(menuActions):
			before = menuActions[pos]
			menu.insertAction( before, action )
		else:
			menu.addAction( action )

		actionMenu.setVisible(True)	# show the menu

		if not self._registeredDbActions.has_key(menuName):
			self._registeredDbActions[menuName] = list()
		self._registeredDbActions[menuName].append(action)

		if callback != None:
			QObject.connect( action, SIGNAL("triggered(bool)"), invoke_callback )

		return True


	def invokeCallback(self, callback, params=None):
		""" Call a method passing the selected item in the database tree,
			the sender (usually a QAction), the plugin mainWindow and
			optionally additional parameters.

			This method takes care to override and restore the cursor,
			but also catches exceptions and displays the error dialog.
		"""
		QApplication.setOverrideCursor(Qt.WaitCursor)
		try:
			if params is None:
				callback( self.tree.currentItem(), self.sender(), self )
			else:
				callback( self.tree.currentItem(), self.sender(), self, *params )

		except BaseError, e:
			# catch database errors and display the error dialog
			DlgDbError.showError(e, self)
			return

		finally:
			QApplication.restoreOverrideCursor()


	def unregisterAction(self, action, menuName):
		if not hasattr(self, '_registeredDbActions'):
			return

		if menuName == None or menuName == "":
			self.removeAction( action )

			if self._registeredDbActions.has_key(menuName):
				if self._registeredDbActions[menuName].count( action ) > 0:
					self._registeredDbActions[menuName].remove( action )

			action.deleteLater()
			return True

		for a in self.menuBar.actions():
			if not a.menu() or a.menu().title() != menuName:
				continue

			menu = a.menu()
			menuActions = menu.actions()

			menu.removeAction( action )
			if menu.isEmpty():	# hide the menu
				a.setVisible(False)

			if self._registeredDbActions.has_key(menuName):
				if self._registeredDbActions[menuName].count( action ) > 0:
					self._registeredDbActions[menuName].remove( action )

				# hide the placeholder if there're no other registered actions
				if len(self._registeredDbActions[menuName]) <= 0:
					for i in range(len(menuActions)):
						if menuActions[i].isSeparator() and menuActions[i].objectName().endsWith("_placeholder"):
							menuActions[i].setVisible(False)
							break


			action.deleteLater()
			return True

		return False

	def unregisterAllActions(self):
		if not hasattr(self, '_registeredDbActions'):
			return

		for menuName in self._registeredDbActions:
			for action in list(self._registeredDbActions[menuName]):
				self.unregisterAction( action, menuName )
		del self._registeredDbActions

	def setupUi(self):
		self.setWindowTitle("DB Manager")
		self.setWindowIcon(QIcon(":/db_manager/icon"))
		self.resize(QSize(700,500).expandedTo(self.minimumSizeHint()))

		# create central tab widget
		self.tabs = QTabWidget()
		self.info = InfoViewer(self)
		self.tabs.addTab(self.info, "Info")
		self.table = TableViewer(self)
		self.tabs.addTab(self.table, "Table")
		self.preview = LayerPreview(self)
		self.tabs.addTab(self.preview, "Preview")
		self.setCentralWidget(self.tabs)

		# create database tree
		self.dock = QDockWidget("Tree", self)
		self.dock.setObjectName("DB_Manager_DBView")
		self.dock.setFeatures(QDockWidget.DockWidgetMovable)
		self.tree = DBTree(self)
		self.dock.setWidget(self.tree)
		self.addDockWidget(Qt.LeftDockWidgetArea, self.dock)

		# create status bar
		self.statusBar = QStatusBar(self)
		self.setStatusBar(self.statusBar)		

		# create menus
		self.menuBar = QMenuBar(self)
		self.menuDb = QMenu("&Database", self)
		actionMenuDb = self.menuBar.addMenu(self.menuDb)
		self.menuSchema = QMenu("&Schema", self)
		actionMenuSchema = self.menuBar.addMenu(self.menuSchema)
		self.menuTable = QMenu("&Table", self)
		actionMenuTable = self.menuBar.addMenu(self.menuTable)
		self.menuHelp = None#QMenu("&Help", self)
		#actionMenuHelp = self.menuBar.addMenu(self.menuHelp)

		self.setMenuBar(self.menuBar)

		# create toolbar
		self.toolBar = QToolBar("Default", self)
		self.toolBar.setObjectName("DB_Manager_ToolBar")
		self.toolBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
		self.addToolBar(self.toolBar)

		# create menus' actions

		# menu DATABASE
		sep = self.menuDb.addSeparator(); sep.setObjectName("DB_Manager_DbMenu_placeholder"); sep.setVisible(False)
		self.actionRefresh = self.menuDb.addAction( QIcon(":/db_manager/actions/refresh"), "&Refresh", self.refreshActionSlot, QKeySequence("F5") )
		self.actionSqlWindow = self.menuDb.addAction( QIcon(":/db_manager/actions/sql_window"), "&SQL window", self.runSqlWindow, QKeySequence("F2") )
		self.menuDb.addSeparator()
		self.actionClose = self.menuDb.addAction( QIcon(), "&Exit", self.close, QKeySequence("CTRL+Q") )

		# menu SCHEMA
		sep = self.menuSchema.addSeparator(); sep.setObjectName("DB_Manager_SchemaMenu_placeholder"); sep.setVisible(False)
		actionMenuSchema.setVisible(False)

		# menu TABLE
		sep = self.menuTable.addSeparator(); sep.setObjectName("DB_Manager_TableMenu_placeholder"); sep.setVisible(False)
		actionMenuTable.setVisible(False)
		self.actionShowSystemTables = self.menuTable.addAction("Show system tables/views", self.showSystemTables)
		self.actionShowSystemTables.setCheckable(True)
		self.actionShowSystemTables.setChecked(True)
		self.actionShowSystemTables.setVisible(False)

		# add actions to the toolbar
		self.toolBar.addAction( self.actionRefresh )
		self.toolBar.addAction( self.actionSqlWindow )

