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

"""
/***************************************************************************
 OpenCageProcessing
                                 A QGIS plugin
 Geocoding using the OpenCage API
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2023-01-11
        copyright            : (C) 2023 by ByteRoad
        email                : info@byteroad.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__ = 'doublebyte'
__date__ = '2023-01-11'
__copyright__ = '(C) 2023 by opencage'

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

__revision__ = '$Format:%H$'

from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterField,
                       QgsProcessingParameterBoolean,
                       QgsProcessingParameterEnum,
                       QgsSettings,
                       QgsField,
                       QgsFields,
                       QgsWkbTypes,
                       QgsCoordinateReferenceSystem,
                       QgsFeature,
                       QgsPoint,
                       QgsProcessingParameterDefinition,
                       QgsProcessingException,
                       QgsProcessingParameterExtent,
                       QgsCoordinateTransform,
                       QgsProject,
                       QgsProcessingParameterFeatureSink)

from .QgsOpenCageGeocoder import QgsOpenCageGeocoder
from .locale_helper import LocaleHelper

import csv

# import logging
# logging.basicConfig(filename='/tmp/opencage.log', encoding='utf-8', level=logging.DEBUG)

class ReverseGeocode(QgsProcessingAlgorithm):
    """
    This algorithm takes a vector file with point geometries
    and creates a geospatial layer with the geocoded addresses.

    In addition, it appends some structured information as attributes.

    As with all Processing algorithms, it extends the QgsProcessingAlgorithm
    class.
    """

    # Constants used to refer to parameters and outputs. They will be
    # used when calling the algorithm from another algorithm, or when
    # calling from the QGIS console.

    OUTPUT = 'Geocoded'
    INPUT = 'INPUT'
    ABBRV = 'Abbreviated?'
    NO_ANNOTATIONS = 'No annotations'
    NO_RECORD = 'No record'
    LANGUAGE = 'Language'
    ADDRESS = 'Address only'

    def initAlgorithm(self, config):
        """
        Here we define the inputs and output of the algorithm, along
        with some other properties.
        """

        # Setup country and language codes, using the helper class.
        self.localhelper = LocaleHelper()

        # We add the input vector features source. It should have
        # a point geometry.

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Point Layer (results will be added as attributes)'),
                [QgsProcessing.TypeVectorPoint] # accepts point geometry, *only*
            )
        )

        abbrvPar = QgsProcessingParameterBoolean(
            self.ABBRV, self.tr('Attempt to abbreviate and shorten the returned address (on the "formatted" field)'), defaultValue=False)
        
        noAnnotationsPar = QgsProcessingParameterBoolean(
            self.NO_ANNOTATIONS, self.tr('Additional information about the result location (e.g.: extra fields). Switch off for faster response!'), defaultValue=False)
    
        noRecordPar = QgsProcessingParameterBoolean(
            self.NO_RECORD, self.tr('Privacy mode: do not log query contents. It may limit customer support.'), defaultValue=False)

        addressOnly = QgsProcessingParameterBoolean(
            self.ADDRESS, self.tr(' Include only the address (exluding POI names) in the formatted string'), defaultValue=False)

        # Codes/names from here: https://en.wikipedia.org/wiki/IETF_language_tag
        # (List of common primary language subtags)
        langPar = QgsProcessingParameterEnum(
                self.LANGUAGE,
                self.tr('Format results in this language, if possible'),
                options=self.localhelper.getLanguageStrings(),
                defaultValue=0,
                optional=False)
        
        abbrvPar.setFlags(abbrvPar.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(abbrvPar)

        noAnnotationsPar.setFlags(noAnnotationsPar.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(noAnnotationsPar)
    
        noRecordPar.setFlags(noRecordPar.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(noRecordPar)

        addressOnly.setFlags(addressOnly.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(addressOnly)

        langPar.setFlags(langPar.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(langPar)

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Geocoded coordinates layer')
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        abbreviation = 1 if self.parameterAsBool(parameters, self.ABBRV, context) == True else 0
        no_annotations = 0 if self.parameterAsBool(parameters, self.NO_ANNOTATIONS, context) == True else 1
        no_record = 1 if self.parameterAsBool(parameters, self.NO_RECORD, context) == True else 0
        address_only = 1 if self.parameterAsBool(parameters, self.ADDRESS, context) == True else 0

        lang_idx= self.parameterAsInt(parameters, self.LANGUAGE, context)
        language = self.localhelper.parseLanguage(lang_idx)

        settings = QgsSettings()
        self.api_key = settings.value('/plugins/opencage/api_key', '', str)

        geocoder = QgsOpenCageGeocoder(self.api_key, False, no_annotations)

        # Retrieve the feature source and sink. The 'dest_id' variable is used
        # to uniquely identify the feature sink, and must be included in the
        # dictionary returned by the processAlgorithm function.
        source = self.parameterAsSource(parameters, self.INPUT, context)

        crs = QgsCoordinateReferenceSystem("EPSG:4326")
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                context, geocoder.appendedFields(), QgsWkbTypes.Point , crs)

        # Compute the number of steps to display within the progress bar and
        # get features from source
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()

        try:
            # Make sure geometries are in WGS84
            xform = QgsCoordinateTransform(source.sourceCrs(),
                                    QgsCoordinateReferenceSystem("EPSG:4326"),
                                      QgsProject.instance())

            for current, feature in enumerate(features):
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    break

                geom = feature.geometry()
                res = geom.transform(xform)
                if res != 0:
                    raise QgsProcessingException
                
                lat = geom.asPoint().y()
                lng = geom.asPoint().x()
                
                new_feature = geocoder.reverse(geom, lat, lng, abbreviation, no_annotations, 
                                               no_record, address_only, language, 
                                               context, feedback)

                if new_feature:
                    sink.addFeature(new_feature, QgsFeatureSink.FastInsert)

                # Update the progress bar
                feedback.setProgress(int(current * total))

            return {self.OUTPUT: dest_id}
    
        except Exception as e:
            feedback.reportError("Error: {}".format(e), True)
            raise QgsProcessingException

    def name(self):
        """
        Returns the algorithm name, used for identifying the algorithm. This
        string should be fixed for the algorithm, and must not be localised.
        The name should be unique within each provider. Names should contain
        lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'r_geocode'

    def displayName(self):
        """
        Returns the translated algorithm name, which should be used for any
        user-visible display of the algorithm name.
        """
        return 'Geocode coordinates'

    def group(self):
        """
        Returns the name of the group this algorithm belongs to. This string
        should be localised.
        """
        # return self.tr(self.groupId())
        return None

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs to. This
        string should be fixed for the algorithm, and must not be localised.
        The group id should be unique within each provider. Group id should
        contain lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return None

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return ReverseGeocode()

    def shortHelpString(self):
        """
        Returns a localised short help string for the algorithm.
        """
        return self.tr('<p>Turn point geometries into human understandable place names or addresses. This process is also known as reverse geocoding.</p> <p>The coordinates used for geocoding are appended as attributes in the output file.</p><p>For information about the other search attributes, please check the help and <a href="https://opencagedata.com/tutorials/geocode-in-qgis">tutorial</a></p>.')
    
    def helpString(self):
        """
        Returns a localised help string for the algorithm.
        """
        return self.tr('Geocoding coordinates')
    
    def helpUrl(self):
        """
        Returns the help url.
        """
        return "https://opencagedata.com/api"
    
    
