"""
/***************************************************************************
Name                 : Virtual layers plugin for DB Manager
Date                 : December 2015
copyright            : (C) 2015 by Hugo Mercier
email                : hugo dot mercier at oslandia dot 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.core import (
    QgsTask,
    QgsVectorLayer,
    QgsVirtualLayerDefinition,
    QgsVirtualLayerTask,
    QgsWkbTypes,
)
from qgis.PyQt.QtCore import QElapsedTimer, QTemporaryFile

from ..data_model import (
    BaseTableModel,
    SqlResultModelAsync,
    SqlResultModelTask,
    TableDataModel,
)
from ..plugin import BaseError, DbError
from .connector import VLayerRegistry, getQueryGeometryName
from .plugin import LVectorTable


class LTableDataModel(TableDataModel):
    def __init__(self, table, parent=None):
        TableDataModel.__init__(self, table, parent)

        self.layer = None

        if isinstance(table, LVectorTable):
            self.layer = VLayerRegistry.instance().getLayer(table.name)
        else:
            self.layer = VLayerRegistry.instance().getLayer(table)

        if not self.layer:
            return
        # populate self.resdata
        self.resdata = []
        for f in self.layer.getFeatures():
            a = f.attributes()
            # add the geometry type
            if f.hasGeometry():
                a.append(QgsWkbTypes.displayString(f.geometry().wkbType()))
            else:
                a.append("None")
            self.resdata.append(a)

        self.fetchedFrom = 0
        self.fetchedCount = len(self.resdata)

    def rowCount(self, index=None):
        if self.layer:
            return self.layer.featureCount()
        return 0


class LSqlResultModelTask(SqlResultModelTask):
    def __init__(self, db, sql, parent):
        super().__init__(db, sql, parent)

        tf = QTemporaryFile()
        tf.open()
        path = tf.fileName()
        tf.close()

        df = QgsVirtualLayerDefinition()
        df.setFilePath(path)
        df.setQuery(sql)

        self.subtask = QgsVirtualLayerTask(df)
        self.addSubTask(
            self.subtask, [], QgsTask.SubTaskDependency.ParentDependsOnSubTask
        )

    def run(self):
        try:
            path = self.subtask.definition().filePath()
            sql = self.subtask.definition().query()
            self.model = LSqlResultModel(self.db, sql, None, self.subtask.layer(), path)
        except Exception as e:
            self.error = BaseError(str(e))
            return False
        return True

    def cancel(self):
        SqlResultModelTask.cancel(self)


class LSqlResultModelAsync(SqlResultModelAsync):
    def __init__(self, db, sql, parent=None):
        super().__init__()

        self.task = LSqlResultModelTask(db, sql, parent)
        self.task.taskCompleted.connect(self.modelDone)
        self.task.taskTerminated.connect(self.modelDone)

    def modelDone(self):
        self.status = self.task.status
        self.model = self.task.model
        if self.task.subtask.exceptionText():
            self.error = BaseError(self.task.subtask.exceptionText())
        self.done.emit()


class LSqlResultModel(BaseTableModel):
    def __init__(self, db, sql, parent=None, layer=None, path=None):
        t = QElapsedTimer()
        t.start()

        if not layer:
            tf = QTemporaryFile()
            tf.open()
            path = tf.fileName()
            tf.close()

            df = QgsVirtualLayerDefinition()
            df.setFilePath(path)
            df.setQuery(sql)
            layer = QgsVectorLayer(df.toString(), "vv", "virtual")
            self._secs = t.elapsed() / 1000.0

        data = []
        header = []

        if not layer.isValid():
            raise DbError(layer.dataProvider().error().summary(), sql)
        else:
            header = [f.name() for f in layer.fields()]
            has_geometry = False
            if layer.geometryType() != QgsWkbTypes.GeometryType.NullGeometry:
                gn = getQueryGeometryName(path)
                if gn:
                    has_geometry = True
                    header += [gn]

            for f in layer.getFeatures():
                a = f.attributes()
                if has_geometry:
                    if f.hasGeometry():
                        a += [f.geometry().asWkt()]
                    else:
                        a += [None]
                data += [a]

        self._secs = 0
        self._affectedRows = len(data)

        BaseTableModel.__init__(self, header, data, parent)

    def secs(self):
        return self._secs

    def affectedRows(self):
        return self._affectedRows
