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

"""
/***************************************************************************
 kisters_processing
                                 A QGIS plugin
 This is the processing toolkit for the network store
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2022-05-28
        copyright            : (C) 2022 by Attila Bibok / KISTERS North America
        email                : attila.bibok@kisters.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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__ = "Attila Bibok / KISTERS North America"
__date__ = "2022-05-28"
__copyright__ = "(C) 2022 by Attila Bibok / KISTERS North America"

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = "$Format:%H$"

import os

from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
    QgsProcessingAlgorithm,
    QgsProcessingException,
    QgsProcessingParameterString,
    QgsProcessingParameterEnum,
    QgsProcessingParameterFileDestination,
    QgsProcessingParameterBoolean,
)

from .network_store_core import (
    load_network_from_store,
    create_network_in_store,
    export_network,
    delete_network_in_store,
)


class _BaseNetworkAlgorithm(QgsProcessingAlgorithm):
    """
    Shared helpers for the network algorithms.
    """

    PARAM_BASE_URL = "BASE_URL"
    PARAM_NETWORK_ID = "NETWORK_ID"

    def createInstance(self):
        # QGIS calls this to clone the algorithm when running it
        return self.__class__()

    def tr(self, string: str) -> str:
        return QCoreApplication.translate("Processing", string)

    # All four will appear under provider "Kisters" / group "Networks"
    def groupId(self) -> str:
        return "Networks"

    def group(self) -> str:
        return self.tr("Networks")

    def base_url_param(self):
        return QgsProcessingParameterString(
            self.PARAM_BASE_URL,
            self.tr("Base URL"),
            defaultValue="http://127.0.0.1:8888",
        )

    def network_id_param(self):
        return QgsProcessingParameterString(
            self.PARAM_NETWORK_ID,
            self.tr("Network ID"),
        )

    def _icon(self, filename: str) -> QIcon:
        return QIcon(os.path.join(os.path.dirname(__file__), filename))

    def flags(self):
        # Ensure this algorithm runs on the main thread
        return super().flags() | QgsProcessingAlgorithm.FlagNoThreading


class NetworkLoadFromStoreAlgorithm(_BaseNetworkAlgorithm):
    """
    Load a network from the store into the current QGIS project.
    """

    def name(self) -> str:
        # internal id, no spaces
        return "load_network_from_store"

    def displayName(self) -> str:
        return self.tr("Load network from store")

    def shortHelpString(self) -> str:
        return self.tr(
            "Loads all WFS layers for a network into the current project, "
            "grouped under <network_id> / {Nodes, Links, Polygons, Controls}."
        )

    def icon(self):
        return self._icon("icon_networkstore_load.png")

    def initAlgorithm(self, config):
        self.addParameter(self.base_url_param())
        self.addParameter(self.network_id_param())

    def processAlgorithm(self, parameters, context, feedback):
        base_url = self.parameterAsString(parameters, self.PARAM_BASE_URL, context)
        network_id = self.parameterAsString(parameters, self.PARAM_NETWORK_ID, context)

        nodes, links, polys, ctrls, ok = load_network_from_store(
            base_url,
            network_id,
            feedback=feedback,
        )

        if not ok:
            raise QgsProcessingException(
                self.tr("No layers were loaded for the given network.")
            )

        # side-effect algorithm, no formal outputs
        return {}


class NetworkCreateInStoreAlgorithm(_BaseNetworkAlgorithm):
    """
    Create a new (empty) network in the store.
    """

    LOAD_NETWORK_PARAM = "LOAD_NETWORK"

    def name(self) -> str:
        return "create_network_in_store"

    def displayName(self) -> str:
        return self.tr("Create new network in store")

    def shortHelpString(self) -> str:
        return self.tr(
            "Creates a new empty network in the network store and loads it "
            "into the current project."
        )

    def icon(self):
        return self._icon("icon_networkstore_add.png")

    def load_network_param(self):
        return QgsProcessingParameterBoolean(
            self.LOAD_NETWORK_PARAM,
            self.tr("Load network into the project"),
            defaultValue=True,
        )

    def initAlgorithm(self, config):
        self.addParameter(self.base_url_param())
        self.addParameter(self.network_id_param())
        self.addParameter(self.load_network_param())

    def processAlgorithm(self, parameters, context, feedback):
        base_url = self.parameterAsString(parameters, self.PARAM_BASE_URL, context)
        network_id = self.parameterAsString(parameters, self.PARAM_NETWORK_ID, context)
        load_network = self.parameterAsBool(
            parameters, self.LOAD_NETWORK_PARAM, context
        )

        ok = create_network_in_store(
            base_url, network_id, load_network=load_network, feedback=feedback
        )
        if not ok:
            raise QgsProcessingException(
                self.tr("Creating the network in the store failed.")
            )
        return {}


class NetworkExportAlgorithm(_BaseNetworkAlgorithm):
    """
    Export a network to a local file.
    """

    PARAM_FORMAT = "FORMAT"
    PARAM_OUTPUT_FILE = "OUTPUT_FILE"

    def name(self) -> str:
        return "export_network"

    def displayName(self) -> str:
        return self.tr("Export network")

    def shortHelpString(self) -> str:
        return self.tr(
            "Exports a network configuration from the network store to a local "
            "file in JSON (v2), old JSON format, or GeoJSON. The network is "
            "not added to the QGIS project."
        )

    def icon(self):
        return self._icon("icon_networkstore_export.png")

    def initAlgorithm(self, config):
        self.addParameter(self.base_url_param())
        self.addParameter(self.network_id_param())

        self.addParameter(
            QgsProcessingParameterEnum(
                self.PARAM_FORMAT,
                self.tr("Export format"),
                options=[
                    self.tr("JSON (v2)"),
                    self.tr("JSON (old format)"),
                    self.tr("GeoJSON"),
                ],
                defaultValue=0,
            )
        )

        self.addParameter(
            QgsProcessingParameterFileDestination(
                self.PARAM_OUTPUT_FILE,
                self.tr("Target file"),
                fileFilter=self.tr(
                    "JSON / GeoJSON (*.json *.geojson);;All files (*.*)"
                ),
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        base_url = self.parameterAsString(parameters, self.PARAM_BASE_URL, context)
        network_id = self.parameterAsString(parameters, self.PARAM_NETWORK_ID, context)

        fmt_idx = self.parameterAsEnum(parameters, self.PARAM_FORMAT, context)
        fmt_map = {0: "json", 1: "json_old_format", 2: "geojson"}
        fmt_literal = fmt_map.get(fmt_idx, "json")

        target_file = self.parameterAsFileOutput(
            parameters, self.PARAM_OUTPUT_FILE, context
        )

        ok = export_network(
            base_url,
            network_id,
            fmt_literal,
            target_file,
            feedback=feedback,
        )
        if not ok:
            raise QgsProcessingException(self.tr("Exporting the network failed."))
        return {}


class NetworkDeleteInStoreAlgorithm(_BaseNetworkAlgorithm):
    """
    Delete a network from the store.
    """

    PARAM_DELETE_ELEMENTS = "DELETE_ELEMENTS"

    def name(self) -> str:
        return "delete_network_in_store"

    def displayName(self) -> str:
        return self.tr("Delete network in store")

    def shortHelpString(self) -> str:
        return self.tr(
            "Deletes a network from the store. If 'Delete elements' is true, "
            "referenced elements are deleted first. Any loaded layers and the "
            "top-level group for this network are removed from the project."
        )

    def icon(self):
        return self._icon("icon_networkstore_delete.png")

    def initAlgorithm(self, config):
        self.addParameter(self.base_url_param())
        self.addParameter(self.network_id_param())
        self.addParameter(
            QgsProcessingParameterBoolean(
                self.PARAM_DELETE_ELEMENTS,
                self.tr(
                    "Delete elements (keep it check unless you know what you are doing)"
                ),
                defaultValue=True,
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        base_url = self.parameterAsString(parameters, self.PARAM_BASE_URL, context)
        network_id = self.parameterAsString(parameters, self.PARAM_NETWORK_ID, context)
        delete_elements = self.parameterAsBool(
            parameters, self.PARAM_DELETE_ELEMENTS, context
        )

        ok = delete_network_in_store(
            base_url,
            network_id,
            delete_elements=delete_elements,
            feedback=feedback,
        )
        if not ok:
            raise QgsProcessingException(
                self.tr("Deleting the network in the store failed.")
            )
        return {}
