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

__author__ = '(C) 2023 by Gerald Kogler'
__date__ = '1/10/2023'
__copyright__ = 'Copyright 2023, 300.000 Km/s'
__license__ = 'GPLv3 license'

import os
import ast
import math

from qgis.core import Qgis, QgsProject, QgsRectangle, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsReferencedRectangle
from qgis.PyQt.QtWidgets import QProgressBar
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import QIcon

class Utils:
    """QuickWebViewer utilities."""

    def getIcon(iconName):
        """Return theme icon with fallback icon."""

        iconPath = os.path.join(os.path.dirname(__file__), "resources", "icons", iconName + ".svg")

        if not os.path.exists(iconPath):
            return QIcon()

        return QIcon(iconPath)


    @staticmethod
    def initProgressBar(maxValue, msg, messageBar):
        """Show progress bar."""

        progressMessageBar = messageBar.createMessage(msg)
        progress = QProgressBar()
        progress.setMaximum(maxValue)
        progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
        progressMessageBar.layout().addWidget(progress)
        messageBar.pushWidget(progressMessageBar, Qgis.Info)

        return progress


    @staticmethod
    def toAsciiAlnum(str):
        """Remove all UTF-8 charactesr from string."""

        str = ''.join(e for e in str if e.isalnum())
        return str.encode('ascii', 'ignore').decode('ascii')


    @staticmethod
    def tr(message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('Utils', message)


    @staticmethod
    def getExtentProj(extentField):
        """Return projection from extent as QgsCoordinateReferenceSystem."""

        if not Utils.isValidExtentField(extentField):
            return False

        try:
            extent = extentField.split(",")
            lastPart = extent[3].split(" ")[1]
            crs = int(lastPart.split(":")[1][0:-1])

            return crs
        except:
            return False

    @staticmethod
    def getExtentRectangle(extentField):
        """Return current value from extent as rectangle."""

        if not Utils.isValidExtentField(extentField):
            return False

        try:
            extent = extentField.split(",")
            lastPart = extent[3].split(" ")
            xMin = float(extent[0])
            xMax = float(extent[1])
            yMin = float(extent[2])
            yMax = float(lastPart[0])
            return QgsRectangle(xMin, yMin, xMax, yMax)
        except:
            return False


    @staticmethod
    def getExtentRectangleProj(extentField, proj):
        """Return current value from extent field as rectangle."""

        try:
            tr = QgsCoordinateTransform(QgsCoordinateReferenceSystem.fromEpsgId(Utils.getExtentProj(extentField)), QgsCoordinateReferenceSystem.fromEpsgId(proj), QgsProject.instance())
            return tr.transform(Utils.getExtentRectangle(extentField))
        except:
            return False


    @staticmethod
    def getExtentBoundsProj(extentField, proj):
        """Return current value from extent field as Bounds string."""

        try:
            extent = Utils.getExtentRectangleProj(extentField, proj)
            return str(extent.xMinimum())+","+str(extent.yMinimum())+","+str(extent.xMaximum())+","+str(extent.yMaximum())
        except:
            return False


    @staticmethod
    def getExtentLngLatBoundsProj(extentField, proj):
        """Return current value from extent field as maplibre like LngLatBounds list."""

        try:
            extent = Utils.getExtentRectangleProj(extentField, proj)
            return ast.literal_eval('[[' + str(extent.xMinimum()) + "," + str(extent.yMinimum()) + "],[" + str(extent.xMaximum()) + "," + str(extent.yMaximum()) + ']]')
        except:
            return False


    @staticmethod
    def isValidExtentField(extentField):
        """Checks if extent field is in the right format."""

        try:
            extent = extentField.split(",")
            lastPart = extent[3].split(" ")
            crsPart = lastPart[1]
            crs = crsPart[1:-1]
            xMin = float(extent[0])
            xMax = float(extent[1])
            yMin = float(extent[2])
            yMax = float(lastPart[0])

            return  isinstance(xMin, float) and \
                    isinstance(xMax, float) and \
                    isinstance(yMin, float) and \
                    isinstance(yMax, float) and \
                    crs.index("EPSG:") != -1 and \
                    isinstance(QgsReferencedRectangle(QgsRectangle(xMin,yMin,xMax,yMax), QgsCoordinateReferenceSystem.fromEpsgId(int(crs[5:]))), QgsReferencedRectangle)

        except:
            return False


    @staticmethod
    def getExtentFormatted(extent, crs):
        """Return extent formatted for extent field."""

        return Utils.getRound(extent.xMinimum())+","+Utils.getRound(extent.xMaximum())+","+Utils.getRound(extent.yMinimum())+","+Utils.getRound(extent.yMaximum())+" ["+crs+"]"


    @staticmethod
    def getRound(num):
        """Return float formatted with 4 decimals."""

        return str("{:.4f}".format(round(num, 4)))


    @staticmethod
    def getZoomFromScale(scale):
        """Calculate web zoom level from map scale factor. Lent from bridgestyle."""

        if scale < 1:  # scale=0 is valid in QGIS
            return 24  # 24 is largest value (according to mapbox spec)
        # val = int(math.log(1000000000 / scale, 2))
        # https://docs.mapbox.com/help/glossary/zoom-level/
        # https://wiki.openstreetmap.org/wiki/Zoom_levels
        # and experimentation
        val = (math.log(279581257 / scale, 2))

        return min(max(val, 0), 24)  # keep between 0 and 24


    @staticmethod
    def toHexColor(qcolor):
        """Convert QColor to hex color. Lent from bridgestyle."""

        try:
            return '#%02x%02x%02x' % (qcolor.red(), qcolor.green(), qcolor.blue())
        except:
            return qcolor