"""
***************************************************************************
    dataobject.py
    ---------------------
    Date                 : August 2012
    Copyright            : (C) 2012 by Victor Olaya
    Email                : volayaf 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__ = "Victor Olaya"
__date__ = "August 2012"
__copyright__ = "(C) 2012, Victor Olaya"

import os
import re

from qgis.core import (
    QgsDataProvider,
    QgsRasterLayer,
    QgsWkbTypes,
    QgsVectorLayer,
    QgsProject,
    QgsSettings,
    QgsProcessingContext,
    QgsProcessingUtils,
    QgsFeatureRequest,
    QgsExpressionContext,
    QgsExpressionContextUtils,
    QgsExpressionContextScope,
)
from qgis.gui import QgsSublayersDialog
from qgis.PyQt.QtCore import QCoreApplication
from qgis.utils import iface

from processing.core.ProcessingConfig import ProcessingConfig

ALL_TYPES = [-1]

TYPE_VECTOR_ANY = -1
TYPE_VECTOR_POINT = 0
TYPE_VECTOR_LINE = 1
TYPE_VECTOR_POLYGON = 2
TYPE_RASTER = 3
TYPE_FILE = 4
TYPE_TABLE = 5


# changing this signature? make sure you update the signature in
# python/processing/__init__.py too!
# Docstring for this function is in python/processing/__init__.py
def createContext(feedback=None):
    context = QgsProcessingContext()
    context.setProject(QgsProject.instance())
    context.setFeedback(feedback)

    invalid_features_method = ProcessingConfig.getSetting(
        ProcessingConfig.FILTER_INVALID_GEOMETRIES
    )
    if invalid_features_method is None:
        invalid_features_method = (
            QgsFeatureRequest.InvalidGeometryCheck.GeometryAbortOnInvalid
        )
    else:
        invalid_features_method = QgsFeatureRequest.InvalidGeometryCheck(
            int(invalid_features_method)
        )
    context.setInvalidGeometryCheck(invalid_features_method)

    settings = QgsSettings()
    context.setDefaultEncoding(
        QgsProcessingUtils.resolveDefaultEncoding(
            settings.value("/Processing/encoding")
        )
    )

    context.setExpressionContext(createExpressionContext())

    if iface and iface.mapCanvas() and iface.mapCanvas().mapSettings().isTemporal():
        context.setCurrentTimeRange(iface.mapCanvas().mapSettings().temporalRange())

    return context


def createExpressionContext():
    context = QgsExpressionContext()
    context.appendScope(QgsExpressionContextUtils.globalScope())
    context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))

    if iface and iface.mapCanvas():
        context.appendScope(
            QgsExpressionContextUtils.mapSettingsScope(iface.mapCanvas().mapSettings())
        )

    processingScope = QgsExpressionContextScope()

    if iface and iface.mapCanvas():
        extent = iface.mapCanvas().fullExtent()
        processingScope.setVariable("fullextent_minx", extent.xMinimum())
        processingScope.setVariable("fullextent_miny", extent.yMinimum())
        processingScope.setVariable("fullextent_maxx", extent.xMaximum())
        processingScope.setVariable("fullextent_maxy", extent.yMaximum())

    context.appendScope(processingScope)
    return context


def load(fileName, name=None, crs=None, style=None, isRaster=False):
    """
    Loads a layer/table into the current project, given its file.

    .. deprecated:: 3.0
    Do not use, will be removed in QGIS 4.0
    """

    from warnings import warn

    warn(
        "processing.load is deprecated and will be removed in QGIS 4.0",
        DeprecationWarning,
    )

    if fileName is None:
        return

    if name is None:
        name = os.path.split(fileName)[1]

    if isRaster:
        options = QgsRasterLayer.LayerOptions()
        options.skipCrsValidation = True
        qgslayer = QgsRasterLayer(fileName, name, "gdal", options)
        if qgslayer.isValid():
            if crs is not None and qgslayer.crs() is None:
                qgslayer.setCrs(crs, False)
            if style is None:
                style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE)
            qgslayer.loadNamedStyle(style)
            QgsProject.instance().addMapLayers([qgslayer])
        else:
            raise RuntimeError(
                QCoreApplication.translate(
                    "dataobject",
                    "Could not load layer: {0}\nCheck the processing framework log to look for errors.",
                ).format(fileName)
            )
    else:
        options = QgsVectorLayer.LayerOptions()
        options.skipCrsValidation = True
        qgslayer = QgsVectorLayer(fileName, name, "ogr", options)
        if qgslayer.isValid():
            if crs is not None and qgslayer.crs() is None:
                qgslayer.setCrs(crs, False)
            if style is None:
                if qgslayer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry:
                    style = ProcessingConfig.getSetting(
                        ProcessingConfig.VECTOR_POINT_STYLE
                    )
                elif qgslayer.geometryType() == QgsWkbTypes.GeometryType.LineGeometry:
                    style = ProcessingConfig.getSetting(
                        ProcessingConfig.VECTOR_LINE_STYLE
                    )
                else:
                    style = ProcessingConfig.getSetting(
                        ProcessingConfig.VECTOR_POLYGON_STYLE
                    )
            qgslayer.loadNamedStyle(style)
            QgsProject.instance().addMapLayers([qgslayer])

    return qgslayer


def getRasterSublayer(path, param):
    layer = QgsRasterLayer(path)

    try:
        # If the layer is a raster layer and has multiple sublayers, let the user chose one.
        # Based on QgisApp::askUserForGDALSublayers
        if (
            layer
            and param.showSublayersDialog
            and layer.dataProvider().name() == "gdal"
            and len(layer.subLayers()) > 1
        ):
            layers = []
            subLayerNum = 0
            # simplify raster sublayer name
            for subLayer in layer.subLayers():
                # if netcdf/hdf use all text after filename
                if bool(re.match("netcdf", subLayer, re.I)) or bool(
                    re.match("hdf", subLayer, re.I)
                ):
                    subLayer = subLayer.split(path)[1]
                    subLayer = subLayer[1:]
                else:
                    # remove driver name and file name
                    subLayer.replace(
                        subLayer.split(QgsDataProvider.SUBLAYER_SEPARATOR)[0], ""
                    )
                    subLayer.replace(path, "")
                # remove any : or " left over
                if subLayer.startswith(":"):
                    subLayer = subLayer[1:]
                if subLayer.startswith('"'):
                    subLayer = subLayer[1:]
                if subLayer.endswith(":"):
                    subLayer = subLayer[:-1]
                if subLayer.endswith('"'):
                    subLayer = subLayer[:-1]

                ld = QgsSublayersDialog.LayerDefinition()
                ld.layerId = subLayerNum
                ld.layerName = subLayer
                layers.append(ld)
                subLayerNum = subLayerNum + 1

            # Use QgsSublayersDialog
            # Would be good if QgsSublayersDialog had an option to allow only one sublayer to be selected
            chooseSublayersDialog = QgsSublayersDialog(
                QgsSublayersDialog.ProviderType.Gdal, "gdal"
            )
            chooseSublayersDialog.populateLayerTable(layers)

            if chooseSublayersDialog.exec():
                return layer.subLayers()[chooseSublayersDialog.selectionIndexes()[0]]
            else:
                # If user pressed cancel then just return the input path
                return path
        else:
            # If the sublayers selection dialog is not to be shown then just return the input path
            return path
    except:
        # If the layer is not a raster layer, then just return the input path
        return path
