import os
import math
import json

_warnings = []


def convertToDict(geostyler):
    global _warnings
    _warnings = []
    global _symbols
    _symbols = []
    layer = processLayer(geostyler)
    return layer, _symbols, _warnings


def convert(geostyler):
    d, _, _ = convertToDict(geostyler)
    mapfile = convertDictToMapfile(d)
    symbols = convertDictToMapfile({"SYMBOLS": _symbols})
    return mapfile, symbols, _warnings


def convertDictToMapfile(d):
    def _toString(element, indent):
        s = ""
        INDENT = "  " * indent
        for k, v in element.items():
            if isinstance(v, dict):
                s += "%s%s\n" % (INDENT, k)
                s += _toString(v, indent + 1)
                s += INDENT + "END\n"
            elif isinstance(v, list):
                for item in v:
                    s += _toString(item, indent)
            elif isinstance(v, tuple):
                s += "%s%s %s\n" % (INDENT, k, " ".join([str(item) for item in v]))
            else:
                s += "%s%s %s\n" % (INDENT, k, v)

        return s

    return _toString(d, 0)


def processLayer(layer):
    classes = []

    for rule in layer.get("rules", []):
        clazz = processRule(rule)
        classes.append(clazz)

    layerData = {
        "LAYER": {
            "NAME": _quote(layer.get("name", "")),
            "STATUS": "ON",
            "SIZEUNITS": "pixels",
            "CLASSES": classes,
        }
    }
    return layerData


def processRule(rule):
    d = {"NAME": _quote(rule.get("name", "") or "default")}
    name = rule.get("name", "rule")

    expression = convertExpression(rule.get("filter", None))
    if expression is not None:
        d["EXPRESSION"] = expression

    styles = [{"STYLE": processSymbolizer(s)} for s in rule["symbolizers"]]

    if "scaleDenominator" in rule:
        scale = rule["scaleDenominator"]
        if "max" in scale:
            d["MAXSCALEDENOM"] = scale["max"]
        if "min" in scale:
            d["MINSCALEDENOM"] = scale["min"]

    d["STYLES"] = styles

    return {"CLASS": d}


func = {
    "Or": "OR",
    "And": "AND",
    "PropertyIsEqualTo": "=",
    "PropertyIsNotEqualTo": "!=",
    "PropertyIsLessThanOrEqualTo": "<=",
    "PropertyIsGreaterThanOrEqualTo": ">=",
    "PropertyIsLessThan": "<",
    "PropertyIsGreaterThan": ">",
    "Add": "+",
    "Sub": "-",
    "Mul": "*",
    "Div": "/",
    "Not": "!",
    "PropertyName": "PropertyName",
}  # TODO


def convertExpression(exp):
    if exp is None:
        return None
    if isinstance(exp, list):
        funcName = func.get(exp[0], None)
        if funcName is None:
            _warnings.append(
                "Unsupported expression function for MapServer conversion: '%s'"
                % exp[0]
            )
            return None
        elif funcName == "PropertyName":
            return '"[%s]"' % exp[1]
        else:
            arg1 = convertExpression(exp[1])
            if len(exp) == 3:
                arg2 = convertExpression(exp[2])
                return "(%s %s %s)" % (arg1, funcName, arg2)
            else:
                return "%s(%s)" % (funcName, arg1)
    else:
        try:
            f = float(exp)
            return exp
        except:
            return _quote(exp)


def processSymbolizer(sl):
    symbolizerType = sl["kind"]
    if symbolizerType == "Icon":
        symbolizer = _iconSymbolizer(sl)
    if symbolizerType == "Line":
        symbolizer = _lineSymbolizer(sl)
    if symbolizerType == "Fill":
        symbolizer = _fillSymbolizer(sl)
    if symbolizerType == "Mark":
        symbolizer = _markSymbolizer(sl)
    if symbolizerType == "Text":
        symbolizer = _textSymbolizer(sl)
    if symbolizerType == "Raster":
        symbolizer = _rasterSymbolizer(sl)

    geom = _geometryFromSymbolizer(sl)
    if geom is not None:
        _warnings.append("Derived geometries are not supported in mapbox gl")

    return symbolizer


def _symbolProperty(sl, name, default=None):
    if name in sl:
        return convertExpression(sl[name])
    else:
        return default


def _textSymbolizer(sl):
    style = {}
    color = _symbolProperty(sl, "color")
    fontFamily = _symbolProperty(sl, "font")
    label = _symbolProperty(sl, "label")
    size = _symbolProperty(sl, "size")
    if "offset" in sl:
        offset = sl["offset"]
        offsetx = convertExpression(offset[0])
        offsety = convertExpression(offset[1])
        style["OFFSET"] = (offsetx, offsety)

    style["TEXT"] = label
    style["SIZE"] = size
    style["FONT"] = fontFamily
    style["TYPE"] = "truetype"
    style["COLOR"] = color

    """
    if "haloColor" in sl and "haloSize" in sl:        
        paint["text-halo-width"] =  _symbolProperty(sl, "haloSize")   
        paint["text-halo-color"] = _symbolProperty(sl, "haloColor")
    
    rotation = -1 * float(qgisLayer.customProperty("labeling/angleOffset"))
    layout["text-rotate"] = rotation

    ["text-opacity"] = (255 - int(qgisLayer.layerTransparency())) / 255.0

    if str(qgisLayer.customProperty("labeling/scaleVisibility")).lower() == "true":
        layer["minzoom"]  = _toZoomLevel(float(qgisLayer.customProperty("labeling/scaleMin")))
        layer["maxzoom"]  = _toZoomLevel(float(qgisLayer.customProperty("labeling/scaleMax")))
    """

    return {"LABEL": style}


def _lineSymbolizer(sl, graphicStrokeLayer=0):
    opacity = _symbolProperty(sl, "opacity", 1.0) * 100
    color = _symbolProperty(sl, "color")
    graphicStroke = sl.get("graphicStroke", None)
    width = _symbolProperty(sl, "width")
    dasharray = _symbolProperty(sl, "dasharray")
    cap = _symbolProperty(sl, "cap")
    join = _symbolProperty(sl, "join")
    offset = _symbolProperty(sl, "offset")

    style = {}
    if graphicStroke is not None:
        name = _createSymbol(graphicStroke[0])  # TODO: support multiple symbol layers
        style["SYMBOL"] = _quote(name)
    if color is not None:
        style["WIDTH"] = width
        style["OPACITY"] = opacity
        style["COLOR"] = color
        style["LINECAP"] = cap
        style["LINEJOIN"] = join
    if dasharray is not None:
        style["PATTERN"] = dasharray
    if offset is not None:
        style["OFFSET"] = "%s -99" % str(offset)

    return style


def _geometryFromSymbolizer(sl):
    geomExpr = convertExpression(sl.get("geometry", None))
    return geomExpr


def _createSymbol(sl):
    name = ""
    symbolizerType = sl["kind"]
    if symbolizerType == "Icon":
        path = os.path.basename(sl["image"])
        name = "icon_" + os.path.splitext(path)[0]
        _symbols.append(
            {"SYMBOL": {"TYPE": "PIXMAP", "IMAGE": _quote(path), "NAME": _quote(name)}}
        )
    elif symbolizerType == "Mark":
        shape = sl["wellKnownName"]
        if shape.startswith("file://"):
            svgFilename = shape.split("//")[-1]
            svgName = os.path.splitext(svgFilename)[0]
            name = "svgicon_" + svgName
            _symbols.append(
                {
                    "SYMBOL": {
                        "TYPE": "svg",
                        "IMAGE": _quote(svgFilename),
                        "NAME": _quote(name),
                    }
                }
            )
        elif shape.startswith("ttf://"):
            token = shape.split("//")[-1]
            font, code = token.split("#")
            character = chr(int(code, 16))
            name = "txtmarker_%s_%s" % (font, character)
            _symbols.append(
                {
                    "SYMBOL": {
                        "TYPE": "TRUETYPE",
                        "CHARACTER": _quote(character),
                        "FONT": _quote(font),
                        "NAME": _quote(name),
                    }
                }
            )
        else:
            name = shape

    return name


def _iconSymbolizer(sl):
    rotation = _symbolProperty(sl, "rotate") or 0
    size = _symbolProperty(sl, "size")
    color = _symbolProperty(sl, "color")
    name = _createSymbol(sl)

    style = {"SYMBOL": _quote(name), "ANGLE": rotation, "SIZE": size}

    return style


def _markSymbolizer(sl):
    # outlineDasharray = _symbolProperty(sl, "strokeDasharray")
    # opacity = _symbolProperty(sl, "opacity")
    size = _symbolProperty(sl, "size")
    rotation = _symbolProperty(sl, "rotate") or 0
    color = _symbolProperty(sl, "color")
    outlineColor = _symbolProperty(sl, "strokeColor")
    outlineWidth = _symbolProperty(sl, "strokeWidth")
    name = _createSymbol(sl)
    style = {"SYMBOL": _quote(name), "COLOR": color, "SIZE": size, "ANGLE": rotation}
    if outlineColor is not None:
        style["OUTLINECOLOR"] = outlineColor
        style["OUTLINEWIDTH"] = outlineWidth

    return style


def _fillSymbolizer(sl):
    style = {}
    opacity = _symbolProperty(sl, "opacity", 1.0) * 100
    color = _symbolProperty(sl, "color")
    graphicFill = sl.get("graphicFill", None)
    if graphicFill is not None:
        name = _createSymbol(graphicFill[0])  # TODO: support multiple symbol layers
        style["SYMBOL"] = _quote(name)
    style["OPACITY"] = opacity
    if color is not None:
        style["COLOR"] = color

    outlineColor = _symbolProperty(sl, "outlineColor")
    if outlineColor is not None:
        outlineWidth = _symbolProperty(sl, "outlineWidth")
        style["OUTLINECOLOR"] = outlineColor
        style["OUTLINEWIDTH"] = outlineWidth

    return style


def _rasterSymbolizer(sl):
    return None


def _quote(t):
    return '"%s"' % t
