#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SPDX-FileCopyrightText: (c) 2018-2021 by mundialis GmbH & Co. KG

SPDX-License-Identifier: Apache-2.0

Module viewer to parser GRASS xml response
"""

__license__ = "Apache-2.0"
__author__ = "Carmen Tawalika"
__copyright__ = "Copyright 2019, mundialis"
__maintainer__ = "Carmen Tawalika"


import collections
import json
import xmltodict

from actinia_module_plugin.model.modules import Module
from actinia_module_plugin.model.modules import ModuleParameter
from actinia_module_plugin.model.modules import ModuleParameterSchema
from actinia_module_plugin.resources.logging import log
from actinia_module_plugin.resources.templating import tplEnv


def logstring(module_id, param, key):
    log.debug(module_id + " " + param + " has no key " + key)
    pass


def setParameterKey(module_id, parameter):
    try:
        key = parameter["@name"]
    except KeyError:
        key = None
        logstring(module_id, key, "name")
    except TypeError:
        logstring(module_id, key, "name")
        log.error("Yet unknown error occured")

    return key


def setVirtualParameterKey(module_id, parameter):
    try:
        key = module_id + "_" + parameter["@name"]
    except KeyError:
        key = None
        logstring(module_id, key, "name")

    return key


def setParameterDescription(module_id, key, parameter, kwargs):
    param_descr = ""
    label = False

    try:
        param_descr = parameter["label"] + ". "
        kwargs["description"] = param_descr
        label = True
    except KeyError:
        # logstring(module_id, key, "label")
        pass
    try:
        param_descr += parameter["description"] + ". "
        kwargs["description"] = param_descr
    except KeyError:
        if label:
            logstring(module_id, key, "description")
            pass
        log.warning(
            "Neither label nor description set for param %s"
            % parameter["@name"]
        )

    return kwargs


def setParameterName(key, kwargs):
    kwargs["name"] = key
    return kwargs


def setParameterOptional(parameter, kwargs):
    try:
        required = parameter["@required"]
        if required == "yes":
            kwargs["optional"] = False
        else:
            kwargs["optional"] = True
    except KeyError:
        required = "False"

    return kwargs


def setParameterDefault(parameter, kwargs):
    try:
        kwargs["default"] = parameter["default"]
        if parameter["default"] is None:
            kwargs["default"] = ""
    except KeyError:
        pass

    return kwargs


def setParamType(module_id, key, parameter, schema_kwargs):
    try:
        # grass parameter types can only be string, double or integer
        gtype = parameter["@type"]
        if gtype in ("float", "double"):
            gtype = "number"
        schema_kwargs["type"] = gtype
    except KeyError:
        logstring(module_id, key, "type")
    try:
        multiple = parameter["@multiple"]
        if multiple == "yes":
            gtype = "array"
        schema_kwargs["type"] = gtype
    except KeyError:
        logstring(module_id, key, "multiple")
    try:
        for subtype_key, val in parameter["gisprompt"].items():
            if subtype_key == "@element":
                schema_kwargs["subtype"] = val
    except Exception:
        pass

    return schema_kwargs


def setParameterEnum(parameter, schema_kwargs):
    try:
        enum = []
        for enum_key, val in parameter["values"].items():
            for item in val:
                for i in item:
                    if i == "name":
                        enum.append(item[i])
        if len(enum) > 0:
            schema_kwargs["enum"] = enum
    except KeyError:
        pass

    return schema_kwargs


def isOutput(parameter):
    """
    Checks if parameter is output parameter.
    Returns True if parameter has key
    'gisprompt.age' == 'new',
    False otherwise.
    """
    try:
        if "@age" in parameter["gisprompt"].keys():
            return parameter["gisprompt"]["@age"] == "new"
        else:
            return False
    except KeyError:
        return False
    except AttributeError:
        return False


def createModuleParameterFromGrassParam(module_id, key, parameter):
    kwargs = dict()
    schema_kwargs = dict()

    schema_kwargs = setParamType(module_id, key, parameter, schema_kwargs)
    kwargs = setParameterDescription(module_id, key, parameter, kwargs)
    kwargs = setParameterName(key, kwargs)
    kwargs = setParameterOptional(parameter, kwargs)
    kwargs = setParameterDefault(parameter, kwargs)
    schema_kwargs = setParameterEnum(parameter, schema_kwargs)

    param_object = ModuleParameter(
        **kwargs, schema=ModuleParameterSchema(**schema_kwargs)
    )

    del kwargs
    del schema_kwargs

    return param_object


def createModuleParameterFromGrassFlag(module_id, parameter):
    kwargs = dict()
    kwargs["default"] = "False"
    kwargs["optional"] = True
    schema_kwargs = dict()
    schema_kwargs["type"] = "boolean"

    key = setParameterKey(module_id, parameter)

    kwargs = setParameterDescription(module_id, key, parameter, kwargs)
    kwargs = setParameterName(key, kwargs)

    param_object = ModuleParameter(
        **kwargs, schema=ModuleParameterSchema(**schema_kwargs)
    )
    del kwargs
    del schema_kwargs

    return param_object


def ParseInterfaceDescription(xml_string, keys=None):
    """
    Parses output of GRASS interface-description
    and returns openEO process object
    """

    gm_dict = xmltodict.parse(xml_string)["task"]

    module_id = gm_dict["@name"]
    description = gm_dict["description"]
    categories = gm_dict["keywords"].replace(" ", "").split(",")
    categories.append("grass-module")
    parameters = []
    returns = []
    extrakwargs = dict()

    # if a GRASS GIS module has only one parameter, xml2dict does not transform
    # parameters to an array but only assigns this one parameter. For our
    # parser to get along, we transform in this case to array as well.
    if "parameter" not in xmltodict.parse(xml_string)["task"]:
        gm_dict["parameter"] = []
    elif (
        type(xmltodict.parse(xml_string)["task"]["parameter"])
        == collections.OrderedDict
        or type(xmltodict.parse(xml_string)["task"]["parameter"]) == dict
    ):
        gm_dict["parameter"] = [gm_dict["parameter"]]
    else:
        # keep as is
        pass

    try:
        grass_params = gm_dict["parameter"]
    except KeyError:
        logstring(module_id, "", "has no parameter")
        grass_params = []

    try:
        flags = gm_dict["flag"]
    except KeyError:
        logstring(module_id, "", "has no flags")
        flags = []

    for parameter in grass_params:
        if keys:
            # case for actinia modules
            key = setVirtualParameterKey(module_id, parameter)
            key_exists = False
            if key not in keys:
                for actiniakey in keys:
                    if actiniakey.startswith(key):
                        key_exists = True
            else:
                key_exists = True
            if not key_exists:
                continue
        else:
            # case for GRASS modules
            key = setParameterKey(module_id, parameter)
        param_object = createModuleParameterFromGrassParam(
            module_id, key, parameter
        )
        if isOutput(parameter):
            returns.append(param_object)
        else:
            parameters.append(param_object)

    for parameter in flags:
        # not possible to specify flag values via template at the moment
        if keys:
            continue
        param_object = createModuleParameterFromGrassFlag(module_id, parameter)

        parameters.append(param_object)

    # custom extention for importer + exporter from actinia_core
    # Runs when viewing importer/exporter as module and when used in template.
    try:
        tpl = tplEnv.get_template("gmodules/" + module_id + ".json")
        pc_template = json.loads(tpl.render().replace("\n", ""))
        for key in [*pc_template]:
            extrakwargs[key] = []
            for param in pc_template[key]:
                extrakwargs[key].append(ModuleParameter(**param))
    except Exception:
        # if no template for module exist, use as is (default)
        # log.debug('template %s does not exist.', e)
        pass

    grass_module = Module(
        id=module_id,
        description=description,
        categories=sorted(categories),
        parameters=parameters,
        returns=returns,
        **extrakwargs,
    )

    return grass_module
