from .basedatatypes import Undefined
from .optional_imports import get_module

np = get_module("numpy")


def _py_to_js(v, widget_manager):
    """
    Python -> Javascript ipywidget serializer

    This function must repalce all objects that the ipywidget library
    can't serialize natively (e.g. numpy arrays) with serializable
    representations

    Parameters
    ----------
    v
        Object to be serialized
    widget_manager
        ipywidget widget_manager (unused)

    Returns
    -------
    any
        Value that the ipywidget library can serialize natively
    """

    # Handle dict recursively
    # -----------------------
    if isinstance(v, dict):
        return {k: _py_to_js(v, widget_manager) for k, v in v.items()}

    # Handle list/tuple recursively
    # -----------------------------
    elif isinstance(v, (list, tuple)):
        return [_py_to_js(v, widget_manager) for v in v]

    # Handle numpy array
    # ------------------
    elif np is not None and isinstance(v, np.ndarray):
        # Convert 1D numpy arrays with numeric types to memoryviews with
        # datatype and shape metadata.
        if (
            v.ndim == 1
            and v.dtype.kind in ["u", "i", "f"]
            and v.dtype != "int64"
            and v.dtype != "uint64"
        ):

            # We have a numpy array the we can directly map to a JavaScript
            # Typed array
            return {"buffer": memoryview(v), "dtype": str(v.dtype), "shape": v.shape}
        else:
            # Convert all other numpy arrays to lists
            return v.tolist()

    # Handle Undefined
    # ----------------
    if v is Undefined:
        return "_undefined_"

    # Handle simple value
    # -------------------
    else:
        return v


def _js_to_py(v, widget_manager):
    """
    Javascript -> Python ipywidget deserializer

    Parameters
    ----------
    v
        Object to be deserialized
    widget_manager
        ipywidget widget_manager (unused)

    Returns
    -------
    any
        Deserialized object for use by the Python side of the library
    """
    # Handle dict
    # -----------
    if isinstance(v, dict):
        return {k: _js_to_py(v, widget_manager) for k, v in v.items()}

    # Handle list/tuple
    # -----------------
    elif isinstance(v, (list, tuple)):
        return [_js_to_py(v, widget_manager) for v in v]

    # Handle Undefined
    # ----------------
    elif isinstance(v, str) and v == "_undefined_":
        return Undefined

    # Handle simple value
    # -------------------
    else:
        return v


# Custom serializer dict for use in ipywidget traitlet definitions
custom_serializers = {"from_json": _js_to_py, "to_json": _py_to_js}
