import inspect
from typing import Any, Dict, Tuple, Union

try:
    from qgis.core import (
        QgsRasterRenderer,
        QgsFeatureRenderer,
        QgsFields,
        QgsFeature,
        QgsVectorLayer,
        QgsGradientColorRamp,
        QgsGradientStop,
        QgsRendererRange,
        QgsSingleBandGrayRenderer,
        QgsPalettedRasterRenderer,
        QgsMultiBandColorRenderer,
        QgsContrastEnhancement,
        QgsSymbol,
        QgsWkbTypes,
        QgsRendererCategory,
        QgsCategorizedSymbolRenderer,
        QgsSingleSymbolRenderer,
        QgsGraduatedSymbolRenderer,
        QgsRasterDataProvider,
    )
    from PyQt5.QtGui import QColor
except ModuleNotFoundError:
    pass

from specklepy.objects.GIS.layers import Layer, RasterLayer, VectorLayer

from speckle.utils.panel_logging import logToUser
from speckle.converter.layers.GISLayerGeometryType import GISLayerGeometryType

# TODO QML format: https://gis.stackexchange.com/questions/202230/loading-style-qml-file-to-layer-via-pyqgis


def featureColorfromNativeRenderer(
    feature: "QgsFeature", layer: "QgsVectorLayer"
) -> int:
    # case with one color for the entire layer
    try:
        renderer = layer.renderer()
        if (
            renderer.type() == "categorizedSymbol"
            or renderer.type() == "25dRenderer"
            or renderer.type() == "invertedPolygonRenderer"
            or renderer.type() == "mergedFeatureRenderer"
            or renderer.type() == "RuleRenderer"
            or renderer.type() == "nullSymbol"
            or renderer.type() == "singleSymbol"
            or renderer.type() == "graduatedSymbol"
        ):
            # get color value
            color = QColor.fromRgb(245, 245, 245)
            if renderer.type() == "singleSymbol":
                color = renderer.symbol().color()
            elif renderer.type() == "categorizedSymbol":
                sSymb = renderer.sourceSymbol()
                if sSymb is not None:
                    color = sSymb.color()
                category = (
                    renderer.classAttribute()
                )  # get the name of attribute used for classification
                try:
                    feature.attribute(category)
                    renderer.categories()
                except:
                    logToUser(
                        f"Attribute '{category}' used for the layer '{layer.name()}' symbology is not found",
                        level=2,
                        func=inspect.stack()[0][3],
                    )
                    return (255 << 24) | (245 << 16) | (245 << 8) | 245

                for obj in renderer.categories():
                    try:
                        if float(obj.value()) == float(feature.attribute(category)):
                            color = obj.symbol().color()
                            break
                    except:
                        if str(obj.value()) == str(feature.attribute(category)):
                            color = obj.symbol().color()
                            break
                    if (
                        str(obj.value()) == "None" or str(obj.value()) == ""
                    ):  # other category
                        color = obj.symbol().color()
            elif renderer.type() == "graduatedSymbol":
                color = renderer.sourceSymbol().color()
                category = (
                    renderer.legendClassificationAttribute()
                )  # get the name of attribute used for classification
                if (
                    renderer.graduatedMethod() == 0
                ):  # if the styling is by color (not by size)
                    for obj in renderer.ranges():
                        if (
                            feature.attribute(category) >= obj.lowerValue()
                            and feature.attribute(category) <= obj.upperValue()
                        ):
                            color = obj.symbol().color()
                            break
            # construct RGB color
            try:
                r, g, b = color.getRgb()[:3]
            except:
                r, g, b = [int(i) for i in color.replace(" ", "").split(",")[:3]]
            col = (255 << 24) | (r << 16) | (g << 8) | b
            return col
        else:
            return (255 << 24) | (245 << 16) | (245 << 8) | 245
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return (255 << 24) | (245 << 16) | (245 << 8) | 245


def gradientColorRampToSpeckle(
    rRamp: "QgsGradientColorRamp",
) -> Union[Dict[str, Any], None]:
    sourceRamp = None
    try:
        props = (
            rRamp.properties()
        )  # {'color1': '255,255,255,255', 'color2': '255,0,0,255', 'discrete': '0', 'rampType': 'gradient'}
        stops = rRamp.stops()  # []
        stopsStr = []
        for s in stops:
            try:
                r, g, b = s.color.getRgb()[:3]
            except:
                r, g, b = [int(i) for i in s.color.replace(" ", "").split(",")[:3]]
            sColor = (255 << 24) | (r << 16) | (g << 8) | b
            stopsStr.append({"color": sColor, "offset": s.offset})
        rampType = rRamp.type()  #'gradient'
        sourceRamp = props
        sourceRamp.update({"stops": stopsStr, "rampType": rampType})

        return sourceRamp
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return sourceRamp


def gradientColorRampToNative(renderer: dict[str, Any]) -> "QgsGradientColorRamp":
    newRamp = None
    try:  # if it's not a random color ramp
        ramp = renderer["properties"]["ramp"]  # {discrete, rampType, stops}
        oldStops = ramp["stops"]
        stops = []
        for i in range(len(oldStops)):
            rgb = oldStops[i]["color"]
            r = (rgb & 0xFF0000) >> 16
            g = (rgb & 0xFF00) >> 8
            b = rgb & 0xFF
            sColor = QColor.fromRgb(r, g, b)
            s = QgsGradientStop(oldStops[i]["offset"], sColor)
            stops.append(s)

        c11, c12, c13, alpa1 = ramp["color1"].split(",")
        color1 = QColor.fromRgb(int(c11), int(c12), int(c13))
        c21, c22, c23, alpha2 = ramp["color2"].split(",")
        color2 = QColor.fromRgb(int(c21), int(c22), int(c23))
        discrete = int(ramp["discrete"])
        newRamp = QgsGradientColorRamp(color1, color2, discrete, stops)

        return newRamp

    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return newRamp


def get_a_r_g_b(argb: int) -> Tuple[int, int, int, int]:
    a = r = g = b = 0
    try:
        a = (argb & 0xFF000000) >> 24
        r = (argb & 0xFF0000) >> 16
        g = (argb & 0xFF00) >> 8
        b = argb & 0xFF
        return a, r, g, b
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return 0, 0, 0, 0


def get_r_g_b(rgb: int) -> Tuple[int, int, int]:
    r = g = b = 0
    try:
        r = (rgb & 0xFF0000) >> 16
        g = (rgb & 0xFF00) >> 8
        b = rgb & 0xFF
        return r, g, b
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return 0, 0, 0


def vectorRendererToNative(
    layer: Union[Layer, VectorLayer], fields: "QgsFields"
) -> Union[
    "QgsSingleSymbolRenderer",
    "QgsCategorizedSymbolRenderer",
    "QgsGraduatedSymbolRenderer",
]:
    rendererNew = None
    try:
        renderer = layer.renderer
        existingAttrs = fields.names()
        geom_type_raw = str(layer.geomType)
        geomType = GISLayerGeometryType.get_native_layer_geometry_type_from_speckle(
            geom_type_raw
        )
        # if geomType == "MultiPatch":
        #    geomType = "Polygon"

        # if "polyline" in geomType.lower():
        #    geomType = "LineString"
        if renderer and renderer["type"]:
            if renderer["type"] == "categorizedSymbol":
                try:
                    _, r, g, b = get_a_r_g_b(renderer["properties"]["sourceSymbColor"])
                except:
                    r = g = b = 100
                sourceSymbColor = QColor.fromRgb(r, g, b)

                attribute = renderer["properties"]["attribute"]
                cats = renderer["properties"]["categories"]
                if attribute not in existingAttrs:
                    rendererNew = makeDefaultRenderer(renderer, layer)
                    return rendererNew
                categories = []
                noneVal = 0
                for i in range(len(cats)):
                    v = cats[i]["value"]
                    if v == "<Null>":
                        v = None
                    if v is None or v == "":
                        noneVal += 1
                    rgb = cats[i]["symbColor"]
                    r = (rgb & 0xFF0000) >> 16
                    g = (rgb & 0xFF00) >> 8
                    b = rgb & 0xFF
                    color = QColor.fromRgb(r, g, b)
                    symbol = QgsSymbol.defaultSymbol(
                        QgsWkbTypes.geometryType(QgsWkbTypes.parseType(geomType))
                    )
                    # create an extra category for possible future feature
                    # if len(categories)==0:
                    #    symbol.setColor(QColor.fromRgb(0,0,0))
                    #    categories.append(QgsRendererCategory())
                    #    categories[0].setSymbol(symbol)
                    #    categories[0].setLabel('Other')

                    symbol.setColor(color)
                    categories.append(
                        QgsRendererCategory(v, symbol, cats[i]["label"], True)
                    )
                # create empty category for all other values (if doesn't exist yet)
                if noneVal == 0:
                    symbol2 = symbol.clone()
                    symbol2.setColor(QColor.fromRgb(0, 0, 0))
                    cat = QgsRendererCategory()
                    cat.setSymbol(symbol2)
                    cat.setLabel("Other")
                    categories.append(cat)

                rendererNew = QgsCategorizedSymbolRenderer(attribute, categories)
                try:
                    sourceSymbol = QgsSymbol.defaultSymbol(
                        QgsWkbTypes.geometryType(QgsWkbTypes.parseType(geomType))
                    )
                    sourceSymbol.setColor(sourceSymbColor)
                    rendererNew.setSourceSymbol(sourceSymbol)
                except:
                    pass

            elif renderer["type"] == "singleSymbol":
                rgb = renderer["properties"]["symbol"]["symbColor"]
                r = (rgb & 0xFF0000) >> 16
                g = (rgb & 0xFF00) >> 8
                b = rgb & 0xFF
                color = QColor.fromRgb(r, g, b)
                symbol = QgsSymbol.defaultSymbol(
                    QgsWkbTypes.geometryType(QgsWkbTypes.parseType(geomType))
                )
                symbol.setColor(color)
                rendererNew = QgsSingleSymbolRenderer(symbol)

            elif renderer["type"] == "graduatedSymbol":
                attribute = renderer["properties"]["attribute"]
                gradMetod = renderer["properties"]["gradMethod"]  # by color or by size
                if attribute not in existingAttrs:
                    rendererNew = makeDefaultRenderer(renderer, layer)
                    return rendererNew

                rgb = renderer["properties"]["sourceSymbColor"]
                r = (rgb & 0xFF0000) >> 16
                g = (rgb & 0xFF00) >> 8
                b = rgb & 0xFF
                sourceSymbColor = QColor.fromRgb(r, g, b)

                if gradMetod == 0:
                    ramp = renderer["properties"]["ramp"]  # {discrete, rampType, stops}
                    ranges = renderer["properties"]["ranges"]  # []
                    newRamp = gradientColorRampToNative(
                        renderer
                    )  # QgsGradientColorRamp

                    newRanges = []
                    for i in range(len(ranges)):
                        rgb = ranges[i]["symbColor"]
                        r = (rgb & 0xFF0000) >> 16
                        g = (rgb & 0xFF00) >> 8
                        b = rgb & 0xFF
                        color = QColor.fromRgb(r, g, b)
                        width = ranges[i]["symbColor"]
                        symbol = QgsSymbol.defaultSymbol(
                            QgsWkbTypes.geometryType(QgsWkbTypes.parseType(geomType))
                        )
                        symbol.setColor(color)
                        newRanges.append(
                            QgsRendererRange(
                                ranges[i]["lower"],
                                ranges[i]["upper"],
                                symbol,
                                ranges[i]["label"],
                                True,
                            )
                        )
                    try:
                        rendererNew = QgsGraduatedSymbolRenderer(attribute, newRanges)
                        rendererNew.setSourceColorRamp(newRamp)
                        sourceSymbol = QgsSymbol.defaultSymbol(
                            QgsWkbTypes.geometryType(QgsWkbTypes.parseType(geomType))
                        )
                        sourceSymbol.setColor(sourceSymbColor)
                        rendererNew.setSourceSymbol(sourceSymbol)
                    except:
                        rendererNew = QgsGraduatedSymbolRenderer()
                    try:
                        rendererNew.setGraduatedMethod(gradMetod)
                    except:
                        rendererNew.setGraduatedMethod(
                            QgsGraduatedSymbolRenderer.GraduatedMethod(gradMetod)
                        )
                else:
                    rendererNew = makeDefaultRenderer(renderer, layer)

        return rendererNew
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return rendererNew


def makeDefaultRenderer(
    renderer: dict[str, Any], layer: Union[Layer, VectorLayer]
) -> "QgsSingleSymbolRenderer":
    rendererNew = None
    try:
        geomType = layer.geomType
        try:
            rgb = renderer["properties"]["sourceSymbColor"]
        except:
            rgb = (255 << 24) | (0 << 16) | (0 << 8) | 0
        r = (rgb & 0xFF0000) >> 16
        g = (rgb & 0xFF00) >> 8
        b = rgb & 0xFF
        color = QColor.fromRgb(r, g, b)
        symbol = QgsSymbol.defaultSymbol(
            QgsWkbTypes.geometryType(QgsWkbTypes.parseType(geomType))
        )
        symbol.setColor(color)
        rendererNew = QgsSingleSymbolRenderer(symbol)
        return rendererNew
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return rendererNew


def rasterRendererToNative(
    layer: RasterLayer, rInterface: "QgsRasterDataProvider"
) -> Union[
    "QgsSingleBandGrayRenderer",
    "QgsMultiBandColorRenderer",
    "QgsPalettedRasterRenderer",
]:
    rendererNew = None
    try:
        renderer = layer.renderer
        if renderer and renderer["type"]:
            if renderer["type"] == "singlebandgray":
                band = renderer["properties"]["band"]
                rendererNew = QgsSingleBandGrayRenderer(rInterface, int(band))
                contrast = QgsContrastEnhancement()
                contrast.setContrastEnhancementAlgorithm(
                    int(renderer["properties"]["contrast"])
                )
                rendererNew.setContrastEnhancement(contrast)
                contrast.setMaximumValue(float(renderer["properties"]["max"]))
                contrast.setMinimumValue(float(renderer["properties"]["min"]))
                # rendererNew.setContrastEnhancement(contrast)

            if renderer["type"] == "multibandcolor":
                redBand = renderer["properties"]["redBand"]
                greenBand = renderer["properties"]["greenBand"]
                blueBand = renderer["properties"]["blueBand"]
                rendererNew = QgsMultiBandColorRenderer(
                    rInterface, int(redBand), int(greenBand), int(blueBand)
                )
                try:
                    contrastR = QgsContrastEnhancement()
                    contrastR.setContrastEnhancementAlgorithm(
                        int(renderer["properties"]["redContrast"])
                    )
                    rendererNew.setRedContrastEnhancement(contrastR)
                    contrastR.setMaximumValue(float(renderer["properties"]["redMax"]))
                    contrastR.setMinimumValue(float(renderer["properties"]["redMin"]))
                    # rendererNew.setRedContrastEnhancement(contrastR)
                except:
                    pass
                try:
                    contrastG = QgsContrastEnhancement()
                    contrastG.setContrastEnhancementAlgorithm(
                        int(renderer["properties"]["greenContrast"])
                    )
                    rendererNew.setGreenContrastEnhancement(contrastG)
                    contrastG.setMaximumValue(float(renderer["properties"]["greenMax"]))
                    contrastG.setMinimumValue(float(renderer["properties"]["greenMin"]))
                    # rendererNew.setGreenContrastEnhancement(contrastG)
                except:
                    pass
                try:
                    contrastB = QgsContrastEnhancement()
                    contrastB.setContrastEnhancementAlgorithm(
                        int(renderer["properties"]["blueContrast"])
                    )
                    rendererNew.setBlueContrastEnhancement(contrastB)
                    contrastB.setMaximumValue(float(renderer["properties"]["blueMax"]))
                    contrastB.setMinimumValue(float(renderer["properties"]["blueMin"]))
                    # rendererNew.setBlueContrastEnhancement(contrastB)
                except:
                    pass

            if renderer["type"] == "paletted":
                band = renderer["properties"]["band"]
                classes = renderer["properties"]["classes"]
                # newRamp = gradientColorRampToNative(renderer) #QgsGradientColorRamp
                newClasses = []
                for i in classes:
                    rgb = i["color"]
                    r = (rgb & 0xFF0000) >> 16
                    g = (rgb & 0xFF00) >> 8
                    b = rgb & 0xFF
                    color = QColor.fromRgb(r, g, b)
                    newClasses.append(
                        QgsPalettedRasterRenderer.Class(
                            float(i["value"]), color, i["label"]
                        )
                    )

                rendererNew = QgsPalettedRasterRenderer(
                    rInterface, int(band), newClasses
                )
        return rendererNew
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return rendererNew


def rendererToSpeckle(
    renderer: Union["QgsFeatureRenderer", "QgsRasterRenderer"],
) -> dict[str, Any]:
    layerRenderer: dict[str, Any] = {}
    if renderer is None:
        return layerRenderer  # e.g. for no-geom layers
    try:
        # print("___RENDERER TO SPECKLE___")
        rType = renderer.type()  # 'singleSymbol','categorizedSymbol','graduatedSymbol',
        layerRenderer["type"] = rType

        if rType == "singleSymbol":
            layerRenderer["properties"] = {"symbol": {}, "symbType": ""}

            symbol = renderer.symbol()  # singleSymbol # QgsLineSymbol
            # print(symbol)
            symbType = symbol.symbolTypeToString(symbol.type())  # Line
            try:
                rgb = symbol.color().getRgb()
            except:
                [int(i) for i in symbol().color().replace(" ", "").split(",")[:3]]
            symbolColor = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]
            layerRenderer["properties"].update(
                {"symbol": {"symbColor": symbolColor}, "symbType": symbType}
            )

        elif rType == "categorizedSymbol":
            layerRenderer["properties"] = {
                "attribute": "",
                "symbType": "",
            }  # {'symbol':{}, 'ramp':{}, 'ranges':{}, 'gradMethod':"", 'symbType':"", 'legendClassificationAttribute': ""}
            attribute = renderer.classAttribute()  # 'id'
            layerRenderer["properties"]["attribute"] = attribute
            symbol = renderer.sourceSymbol()
            sourceSymbColor = (255 << 24) | (0 << 16) | (0 << 8) | 0
            try:
                symbType = symbol.symbolTypeToString(symbol.type())  # Line
                try:
                    r, g, b = symbol.color().getRgb()[:3]
                except:
                    r, g, b = [
                        int(i) for i in symbol.color().replace(" ", "").split(",")[:3]
                    ]
                sourceSymbColor = (255 << 24) | (r << 16) | (g << 8) | b

                layerRenderer["properties"].update(
                    {"symbType": symbType, "sourceSymbColor": sourceSymbColor}
                )
            except:
                pass

            categories = (
                renderer.categories()
            )  # <qgis._core.QgsRendererCategory object at 0x00000155E8786A60>
            layerRenderer["properties"]["categories"] = []
            for i in categories:
                value = i.value()
                try:
                    r, g, b = i.symbol().color().getRgb()[:3]
                except:
                    r, g, b = [
                        int(i)
                        for i in i.symbol().color().replace(" ", "").split(",")[:3]
                    ]
                symbColor = (255 << 24) | (r << 16) | (g << 8) | b
                symbOpacity = i.symbol().opacity()  # QgsSymbol.color()
                label = i.label()
                layerRenderer["properties"]["categories"].append(
                    {
                        "value": value,
                        "symbColor": symbColor,
                        "symbOpacity": symbOpacity,
                        "sourceSymbColor": sourceSymbColor,
                        "label": label,
                    }
                )

        elif rType == "graduatedSymbol":
            layerRenderer["properties"] = {
                "symbol": {},
                "ramp": {},
                "ranges": {},
                "gradMethod": "",
                "symbType": "",
            }

            attribute = renderer.legendClassificationAttribute()  # 'id'
            symbol = renderer.sourceSymbol()  # QgsLineSymbol
            symbType = symbol.symbolTypeToString(symbol.type())  # Line
            try:
                r, g, b = symbol.color().getRgb()[:3]
            except:
                r, g, b = [
                    int(i) for i in symbol.color().replace(" ", "").split(",")[:3]
                ]
            sourceSymbColor = (255 << 24) | (r << 16) | (g << 8) | b
            gradMethod = renderer.graduatedMethod()  # 0
            layerRenderer["properties"].update(
                {
                    "attribute": attribute,
                    "symbType": symbType,
                    "gradMethod": gradMethod,
                    "sourceSymbColor": sourceSymbColor,
                }
            )

            rRamp = renderer.sourceColorRamp()  # QgsGradientColorRamp
            if isinstance(rRamp, QgsGradientColorRamp):
                layerRenderer["properties"]["ramp"] = gradientColorRampToSpeckle(rRamp)

            rRanges = renderer.ranges()  # [QgsRendererRange,...]
            layerRenderer["properties"]["ranges"] = []
            for i in rRanges:
                if isinstance(i, QgsRendererRange):
                    lower = i.lowerValue()
                    upper = i.upperValue()
                    rgb = i.symbol().color().getRgb()  # QgsSymbol.color() -> QColor
                    symbColor = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]
                    symbOpacity = i.symbol().opacity()  # QgsSymbol.color()
                    label = i.label()
                    width = 0.26
                    try:
                        width = i.width()
                    except:
                        pass
                    # {'label': '1 - 1.4', 'lower': 1.0, 'symbColor': <PyQt5.QtGui.QColor ...BD9B9D4A0>, 'symbOpacity': 1.0, 'upper': 1.4}
                    layerRenderer["properties"]["ranges"].append(
                        {
                            "lower": lower,
                            "upper": upper,
                            "symbColor": symbColor,
                            "symbOpacity": symbOpacity,
                            "label": label,
                            "width": width,
                        }
                    )

        elif rType == "singlebandgray":
            band = renderer.grayBand()
            contrast = renderer.contrastEnhancement().contrastEnhancementAlgorithm()
            mmin = renderer.contrastEnhancement().minimumValue()
            mmax = renderer.contrastEnhancement().maximumValue()
            layerRenderer.update(
                {
                    "properties": {
                        "max": mmax,
                        "min": mmin,
                        "band": band,
                        "contrast": contrast,
                    }
                }
            )
        elif rType == "multibandcolor":
            redBand = renderer.redBand()
            greenBand = renderer.greenBand()
            blueBand = renderer.blueBand()
            redContrast = redMin = redMax = greenContrast = greenMin = greenMax = (
                blueContrast
            ) = blueMin = blueMax = None
            try:
                redContrast = (
                    renderer.redContrastEnhancement().contrastEnhancementAlgorithm()
                )
                redMin = renderer.redContrastEnhancement().minimumValue()
                redMax = renderer.redContrastEnhancement().maximumValue()
            except:  # AttributeError: 'NoneType' object has no attribute 'contrastEnhancementAlgorithm'
                pass
            try:
                greenContrast = (
                    renderer.greenContrastEnhancement().contrastEnhancementAlgorithm()
                )
                greenMin = renderer.greenContrastEnhancement().minimumValue()
                greenMax = renderer.greenContrastEnhancement().maximumValue()
            except:  # AttributeError
                pass
            try:
                blueContrast = (
                    renderer.blueContrastEnhancement().contrastEnhancementAlgorithm()
                )
                blueMin = renderer.blueContrastEnhancement().minimumValue()
                blueMax = renderer.blueContrastEnhancement().maximumValue()
            except:  # AttributeError
                pass
            layerRenderer.update(
                {
                    "properties": {
                        "greenBand": greenBand,
                        "blueBand": blueBand,
                        "redBand": redBand,
                    }
                }
            )
            layerRenderer["properties"].update(
                {"redContrast": redContrast, "redMin": redMin, "redMax": redMax}
            )
            layerRenderer["properties"].update(
                {
                    "greenContrast": greenContrast,
                    "greenMin": greenMin,
                    "greenMax": greenMax,
                }
            )
            layerRenderer["properties"].update(
                {"blueContrast": blueContrast, "blueMin": blueMin, "blueMax": blueMax}
            )

        elif rType == "paletted":
            band = renderer.band()
            rendererClasses = renderer.classes()
            classes = []
            rRamp = renderer.sourceColorRamp()
            sourceRamp = {}
            if isinstance(rRamp, QgsGradientColorRamp):  # or QgsRandomColorRamp
                sourceRamp = gradientColorRampToSpeckle(
                    rRamp
                )  # rampType, stops,props(e.g.color)

            for i in rendererClasses:
                value = i.value
                rgb = i.color.getRgb()
                color = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]
                classes.append({"color": color, "value": value, "label": i.label})
            layerRenderer.update(
                {"properties": {"classes": classes, "ramp": sourceRamp, "band": band}}
            )

        else:
            layerRenderer = {"type": "Other", "properties": {}}
        return layerRenderer
    except Exception as e:
        logToUser(e, level=2, func=inspect.stack()[0][3])
        return layerRenderer
