import typing
import struct


def tg_checkSum64(data: typing.Union[bytes, bytearray], seed: int = 0):
    """This function creates a TGDB standard checksum.

    This checksum is based on the MurMur64 hashing algorithm.

    :param data: The data bytes to find the hash of.
    :type data: bytes-like
    :param seed: a 64-bit integer that represents a fixed seed to base the checksum off of (default is 0).
    :type seed: int

    :returns: a 64-bit integer representing the MurMur64 hash of the input data and the seed.
    :rtype: int
    """

    def __limit(input: int):
        return input & 0xFFFFFFFFFFFFFFFF

    m = 0xc6a4a7935bd1e995
    r = 47

    length = len(data)

    hash = __limit(seed) ^ __limit(length*m)

    start = 0
    end = length - (length % 8)

    while start != end:
        cur_data: int = struct.unpack_from("<Q", data, start)[0]

        cur_data = __limit(cur_data * m)
        cur_data = __limit(cur_data ^ (cur_data >> r))
        cur_data = __limit(cur_data * m)

        hash = __limit(cur_data ^ hash)
        hash = __limit(hash * m)

        start += 8

    rem = length & 0b111

    # Quite a bit less tidy than the switch statement in the C version, but Python is particular about some things...
    # Maybe move to same line to same line to reflect C version?
    if rem > 6:
        hash ^= (data[end + 6] << 48)
    if rem > 5:
        hash ^= (data[end + 5] << 40)
    if rem > 4:
        hash ^= (data[end + 4] << 32)
    if rem > 3:
        hash ^= (data[end + 3] << 24)
    if rem > 2:
        hash ^= (data[end + 2] << 16)
    if rem > 1:
        hash ^= (data[end + 1] << 8)
    if rem > 0:
        hash ^= (data[end + 0] << 0)
        hash = __limit(hash * m)

    hash = __limit(hash)

    hash ^= hash >> r
    hash = __limit(hash * m)
    hash ^= hash >> r

    return __limit(hash)