from json import JSONEncoder
from numbers import Real, Integral
import numpy as np


class UniversalJSONEncoder(JSONEncoder):
    """
    A json encoder which encodes almost everything.

    Supports:

       * everything with a __dict__ attribute
       * numpy
       * standard dict, list, scalar, boolean
    """
    MAX_NP_ARRAY_LENGTH = 100

    def default(self, obj):
        try:
            return super().default(obj)
        except TypeError:
            pass

        try:
            dct = obj.__dict__.copy()
            dct["__class__.__name__"] = obj.__class__.__name__
            return dct
        except AttributeError:
            pass

        r = repr(obj)
        if r == "False":
            return False
        if r == "True":
            return True

        if isinstance(obj, np.ndarray):
            if obj.shape == ():
                return float(obj)
            else:
                return list(obj[:self.MAX_NP_ARRAY_LENGTH])

        if isinstance(obj, Integral):
            return int(obj)

        if isinstance(obj, Real):
            return float(obj)


        raise TypeError("Can not encode {}".format(obj))
