"""Interactive dashboard wrapper."""

from .KernelPart import KernelPart, register_part
from ..serialization import guess_type, serialize
from IPython.display import display
from uuid import uuid4

__all__ = [
    "wrap"
]

TYPE_TO_PART = {
    "Number": "SliderPart",
    "String": "TextAreaPart",
    "Date": "DatePickerPart",
    "DateTime": "DatePickerPart",
    "Table": "TableEditor",
    "Boolean": "CheckboxPart"
}

options_for_parts = {
    "SliderPart": ["Value", {
        "Min": serialize(0, "Number"),
        "Max": serialize(500, "Number"),
        "Step": serialize(10, "Number")
    }],
    "TableEditor": ["Input Data", {}],
    "CheckboxPart": ["Checked", {}]
}


def make_part(partType, global_binding_name):
    override, options = options_for_parts[partType] \
        if partType in options_for_parts else ["Value", {}]
    options = options.copy()
    options[override] = {
        "type": "Global",
        "expr": global_binding_name,
        "globals": [global_binding_name]
    }
    return {"application/vnd.maven.part+json": {
        "name": partType,
        "options": options
    }}


def make_wrapper_part(fn, argTypes):
    name = "__autogenerated_" + str(uuid4())
    @register_part(name)  # pylint: disable=unused-variable
    class AutoGeneratedPart(KernelPart):
        @classmethod
        def get_metadata(cls):
            metadata = super().get_metadata()
            for i, argType in enumerate(argTypes):
                metadata.add_option("arg" + str(i), None, argType)

            return metadata

        def render(self, opts):
            vals = [opts[arg] for arg in opts]
            return fn(*vals)

    return name


def wrap(fn, *args):
    """Wrap a function with an interactive MavenWorks dashboard.

    :param fn: A function to wrap, that either returns or ``display()``s some
    output.

    :param args: The initial values to pass to the function. Each argument
    provided here will be bound to an input part.

    """
    parts = {}
    global_defs = []
    arg_types = []
    layout = {
        "properties": {},
        "typeName": 0,
        "uuid": "root",
        "children": []
    }
    for i, arg in enumerate(args):
        arg_type = guess_type(arg)
        if arg_type not in TYPE_TO_PART:
            pass
        arg_types.append(arg_type)
        global_defs.append({
            "name": "arg" + str(i),
            "type": arg_type,
            "value": serialize(arg, arg_type)["value"]
        })
        part_id = str(uuid4())
        parts[part_id] = make_part(TYPE_TO_PART[arg_type], "arg" + str(i))
        layout["children"].append({
            "properties": {
                "caption": "Arg" + str(i)
            },
            "typeName": 1,
            "uuid": str(uuid4()),
            "guid": part_id
        })
    new_part = make_wrapper_part(fn, arg_types)
    part_id = str(uuid4())
    parts[part_id] = {"application/vnd.maven.part+json": {
        "name": new_part,
        "options": {"arg" + str(i): {
            "type": "Global",
            "expr": "arg" + str(i),
            "globals": ["arg" + str(i)]
        } for i in range(len(arg_types))}
    }}
    layout["children"].append({
        "properties": {
            "caption": "Output"
        },
        "typeName": 1,
        "uuid": str(uuid4()),
        "guid": part_id
    })
    display({"application/vnd.maven.layout+json": {
        "layout": layout,
        "globals": global_defs,
        "parts": parts
    }}, raw=True)
