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

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 qgis.PyQt.QtCore import pyqtSignal, QCoreApplication
from qgis.PyQt.QtWidgets import QWidget, QTreeView, QMenu, QLabel

from qgis.core import Qgis, QgsProject, QgsMessageLog
from qgis.gui import QgsMessageBar, QgsMessageBarItem

from .db_model import DBModel, PluginItem
from .db_plugins.gpkg.plugin import GPKGRasterTable
from .db_plugins.plugin import DBPlugin, Schema, Table
from .db_plugins.vlayers.plugin import LTable

from osgeo import gdal


class DBTree(QTreeView):
    selectedItemChanged = pyqtSignal(object)

    def __init__(self, mainWindow):
        QTreeView.__init__(self, mainWindow)
        self.mainWindow = mainWindow

        self.setModel(DBModel(self))
        self.setHeaderHidden(True)
        self.setEditTriggers(
            QTreeView.EditTrigger.EditKeyPressed | QTreeView.EditTrigger.SelectedClicked
        )

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.setDropIndicatorShown(True)

        self.doubleClicked.connect(self.addLayer)
        self.selectionModel().currentChanged.connect(self.currentItemChanged)
        self.expanded.connect(self.itemChanged)
        self.collapsed.connect(self.itemChanged)
        self.model().dataChanged.connect(self.modelDataChanged)
        self.model().notPopulated.connect(self.collapse)

    def refreshItem(self, item=None):
        if item is None:
            item = self.currentItem()
            if item is None:
                return
        self.model().refreshItem(item)

    def showSystemTables(self, show):
        pass

    def currentItem(self):
        indexes = self.selectedIndexes()
        if len(indexes) <= 0:
            return
        return self.model().getItem(indexes[0])

    def currentDatabase(self):
        item = self.currentItem()
        if item is None:
            return

        if isinstance(item, (DBPlugin, Schema, Table)):
            return item.database()
        return None

    def currentSchema(self):
        item = self.currentItem()
        if item is None:
            return

        if isinstance(item, (Schema, Table)):
            return item.schema()
        return None

    def currentTable(self):
        item = self.currentItem()

        if isinstance(item, Table):
            return item
        return None

    def newConnection(self):
        index = self.currentIndex()
        if not index.isValid() or not isinstance(index.internalPointer(), PluginItem):
            return
        item = self.currentItem()
        self.mainWindow.invokeCallback(item.addConnectionActionSlot, index)

    def itemChanged(self, index):
        self.setCurrentIndex(index)
        self.selectedItemChanged.emit(self.currentItem())

    def modelDataChanged(self, indexFrom, indexTo):
        self.itemChanged(indexTo)

    def currentItemChanged(self, current, previous):
        self.itemChanged(current)

    def contextMenuEvent(self, ev):
        index = self.indexAt(ev.pos())
        if not index.isValid():
            return

        if index != self.currentIndex():
            self.itemChanged(index)

        item = self.currentItem()

        menu = QMenu(self)

        if isinstance(item, (Table, Schema)) and not isinstance(item, LTable):
            if not (
                isinstance(item, GPKGRasterTable) and int(gdal.VersionInfo()) < 3100000
            ):
                menu.addAction(
                    QCoreApplication.translate("DBTree", "Rename…"), self.rename
                )
            menu.addAction(QCoreApplication.translate("DBTree", "Delete…"), self.delete)

            if isinstance(item, Table) and item.canBeAddedToCanvas():
                menu.addSeparator()
                menu.addAction(self.tr("Add to Canvas"), self.addLayer)
                item.addExtraContextMenuEntries(menu)

        elif isinstance(item, DBPlugin):
            if item.database() is not None:
                menu.addAction(self.tr("Re-connect"), self.reconnect)
            menu.addAction(self.tr("Remove"), self.delete)

        elif not index.parent().isValid() and item.typeName() in ("spatialite", "gpkg"):
            menu.addAction(
                QCoreApplication.translate("DBTree", "New Connection…"),
                self.newConnection,
            )

        if not menu.isEmpty():
            menu.exec(ev.globalPos())

        menu.deleteLater()

    def rename(self):
        item = self.currentItem()
        if isinstance(item, (Table, Schema)):
            self.edit(self.currentIndex())

    def delete(self):
        item = self.currentItem()
        if isinstance(item, (Table, Schema)):
            self.mainWindow.invokeCallback(item.database().deleteActionSlot)
        elif isinstance(item, DBPlugin):
            self.mainWindow.invokeCallback(item.removeActionSlot)

    def addLayer(self):
        table = self.currentTable()
        if table is not None:
            layer = table.toMapLayer(table.geometryType())
            layers = QgsProject.instance().addMapLayers([layer])
            if len(layers) != 1:
                QgsMessageLog.logMessage(
                    self.tr("%1 is an invalid layer - not loaded").replace(
                        "%1", layer.publicSource()
                    )
                )
                msgLabel = QLabel(
                    self.tr(
                        '%1 is an invalid layer and cannot be loaded. Please check the <a href="#messageLog">message log</a> for further info.'
                    ).replace("%1", layer.publicSource()),
                    self.mainWindow.infoBar,
                )
                msgLabel.setWordWrap(True)
                msgLabel.linkActivated.connect(
                    self.mainWindow.iface.mainWindow()
                    .findChild(QWidget, "MessageLog")
                    .show
                )
                msgLabel.linkActivated.connect(
                    self.mainWindow.iface.mainWindow().raise_
                )
                self.mainWindow.infoBar.pushItem(
                    QgsMessageBarItem(msgLabel, Qgis.MessageLevel.Warning)
                )

    def reconnect(self):
        db = self.currentDatabase()
        if db is not None:
            self.mainWindow.invokeCallback(db.reconnectActionSlot)
