import math
import struct

import msgpack
import orjson


def uint2bytes(ulong):
    return struct.pack(">I", ulong)


def bytes2uint(bs):
    return struct.unpack(">I", bs)[0]


def ulong2bytes(ulong):
    return struct.pack(">Q", ulong)


def bytes2ulong(bs):
    return struct.unpack(">Q", bs)[0]


def msgpack_lite_ext_hook(code, data):
    # The msgpack-lite library uses extension types for the typed javascript arrays.
    # https://github.com/kawanet/msgpack-lite/blob/master/lib/ext-packer.js#L28
    if code == 18:  # Uint8Array; the primary one we care about
        return data
    if code == 26:  # ArrayBuffer; Uint8Array really, but used for file reading
        return data
    return msgpack.ExtType(code, data)


def msgpack_dumps(obj):
    return msgpack.packb(obj, use_bin_type=True)


def msgpack_loads(b):
    return msgpack.unpackb(b, raw=False, strict_map_key=False, ext_hook=msgpack_lite_ext_hook)


def msgpack_loads_strict_map_key(b):
    return msgpack.unpackb(b, raw=False, ext_hook=msgpack_lite_ext_hook)


def bigger_int_from_js_msgpack(f):
    """
    The JS msgpack library takes a shortcut or conforms to the asinine JS engine native behaviour and packs
    integers that are not in the signed-integer32 range as IEEE754 float64. This does not lose precision
    as long as the float itself does not (Number.MAX_SAFE_INTEGER), but the type is changed. Handling this by
    a work-around is not desirable, but fixing/changing the library would be harder right now.
    """
    if isinstance(f, float):
        fraction, integer = math.modf(f)
        if fraction == 0:
            return int(integer)
    return f


def json_dumps(obj):
    return orjson.dumps(obj)


def json_loads(b):
    return orjson.loads(b)
