"""
***************************************************************************
    AlgorithmLocatorFilter.py
    -------------------------
    Date                 : May 2017
    Copyright            : (C) 2017 by Nyall Dawson
    Email                : nyall dot dawson at gmail 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.                                   *
*                                                                         *
***************************************************************************
"""

__author__ = "Nyall Dawson"
__date__ = "May 2017"
__copyright__ = "(C) 2017, Nyall Dawson"

from qgis.core import (
    QgsApplication,
    QgsFields,
    QgsLocatorFilter,
    QgsLocatorResult,
    QgsMapLayerType,
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingFeatureBasedAlgorithm,
    QgsStringUtils,
    QgsWkbTypes,
)
from qgis.utils import iface

from processing.core.ProcessingConfig import ProcessingConfig
from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.gui.AlgorithmExecutor import execute_in_place
from processing.gui.MessageBarProgress import MessageBarProgress
from processing.gui.MessageDialog import MessageDialog


class AlgorithmLocatorFilter(QgsLocatorFilter):
    def __init__(self, parent=None):
        super().__init__(parent)

    def clone(self):
        return AlgorithmLocatorFilter()

    def name(self):
        return "processing_alg"

    def displayName(self):
        return self.tr("Processing Algorithms")

    def priority(self):
        return QgsLocatorFilter.Priority.Low

    def prefix(self):
        return "a"

    def flags(self):
        return QgsLocatorFilter.Flag.FlagFast

    def fetchResults(self, string, context, feedback):
        # collect results in main thread, since this method is inexpensive and
        # accessing the processing registry is not thread safe
        for a in QgsApplication.processingRegistry().algorithms():
            if a.flags() & QgsProcessingAlgorithm.Flag.FlagHideFromToolbox:
                continue
            if (
                not ProcessingConfig.getSetting(
                    ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES
                )
                and a.flags() & QgsProcessingAlgorithm.Flag.FlagKnownIssues
            ):
                continue

            result = QgsLocatorResult()
            result.filter = self
            result.displayString = a.displayName()
            result.icon = a.icon()
            result.userData = a.id()
            result.score = 0

            if context.usingPrefix and not string:
                self.resultFetched.emit(result)

            if not string:
                return

            string = string.lower()
            tagScore = 0
            if a.provider():
                tags = [*a.tags(), a.provider().name()]
            else:
                tags = a.tags()

            if a.group():
                tags.append(a.group())

            for t in tags:
                if string in t.lower():
                    tagScore = 1
                    break

            result.score = (
                QgsStringUtils.fuzzyScore(result.displayString, string) * 0.5
                + tagScore * 0.5
            )

            if result.score > 0:
                self.resultFetched.emit(result)

    def triggerResult(self, result):
        alg = QgsApplication.processingRegistry().createAlgorithmById(result.userData)
        if not alg:
            return

        ok, message = alg.canExecute()
        if not ok:
            dlg = MessageDialog()
            dlg.setTitle(self.tr("Missing dependency"))
            dlg.setMessage(message)
            dlg.exec()
            return
        dlg = alg.createCustomParametersWidget(parent=iface.mainWindow())
        if not dlg:
            dlg = AlgorithmDialog(alg, parent=iface.mainWindow())
        canvas = iface.mapCanvas()
        prevMapTool = canvas.mapTool()
        dlg.show()
        dlg.exec()
        if canvas.mapTool() != prevMapTool:
            try:
                canvas.mapTool().reset()
            except:
                pass
            try:
                canvas.setMapTool(prevMapTool)
            except RuntimeError:
                pass


class InPlaceAlgorithmLocatorFilter(QgsLocatorFilter):
    def __init__(self, parent=None):
        super().__init__(parent)

    def clone(self):
        return InPlaceAlgorithmLocatorFilter()

    def name(self):
        return "edit_features"

    def displayName(self):
        return self.tr("Edit Features In-Place")

    def priority(self):
        return QgsLocatorFilter.Priority.Low

    def prefix(self):
        return "ef"

    def flags(self):
        return QgsLocatorFilter.Flag.FlagFast

    def fetchResults(self, string, context, feedback):
        # collect results in main thread, since this method is inexpensive and
        # accessing the processing registry/current layer is not thread safe

        if (
            iface.activeLayer() is None
            or iface.activeLayer().type() != QgsMapLayerType.VectorLayer
        ):
            return

        for a in QgsApplication.processingRegistry().algorithms():
            if not a.flags() & QgsProcessingAlgorithm.Flag.FlagSupportsInPlaceEdits:
                continue

            if not a.supportInPlaceEdit(iface.activeLayer()):
                continue

            result = QgsLocatorResult()
            result.filter = self
            result.displayString = a.displayName()
            result.icon = a.icon()
            result.userData = a.id()
            result.score = 0

            if context.usingPrefix and not string:
                self.resultFetched.emit(result)

            if not string:
                return

            string = string.lower()
            tagScore = 0
            if a.provider():
                tags = [*a.tags(), a.provider().name()]
            else:
                tags = a.tags()

            if a.group():
                tags.append(a.group())

            for t in tags:
                if string in t.lower():
                    tagScore = 1
                    break

            result.score = (
                QgsStringUtils.fuzzyScore(result.displayString, string) * 0.5
                + tagScore * 0.5
            )

            if result.score > 0:
                self.resultFetched.emit(result)

    def triggerResult(self, result):
        config = {"IN_PLACE": True}
        alg = QgsApplication.processingRegistry().createAlgorithmById(
            result.userData, config
        )
        if not alg:
            return

        ok, message = alg.canExecute()
        if not ok:
            dlg = MessageDialog()
            dlg.setTitle(self.tr("Missing dependency"))
            dlg.setMessage(message)
            dlg.exec()
            return

        in_place_input_parameter_name = "INPUT"
        if hasattr(alg, "inputParameterName"):
            in_place_input_parameter_name = alg.inputParameterName()

        if [
            d
            for d in alg.parameterDefinitions()
            if d.name() not in (in_place_input_parameter_name, "OUTPUT")
        ]:
            dlg = alg.createCustomParametersWidget(parent=iface.mainWindow())
            if not dlg:
                dlg = AlgorithmDialog(alg, True, parent=iface.mainWindow())
            canvas = iface.mapCanvas()
            prevMapTool = canvas.mapTool()
            dlg.show()
            dlg.exec()
            if canvas.mapTool() != prevMapTool:
                try:
                    canvas.mapTool().reset()
                except:
                    pass
                canvas.setMapTool(prevMapTool)
        else:
            feedback = MessageBarProgress(algname=alg.displayName())
            parameters = {}
            ok, results = execute_in_place(alg, parameters, feedback=feedback)
            if ok:
                iface.messageBar().pushSuccess(
                    "",
                    self.tr(
                        "{algname} completed. %n feature(s) processed.",
                        n=results["__count"],
                    ).format(algname=alg.displayName()),
                )
            feedback.close()
