import struct


class UnknownArgumentType(Exception):
    pass


class PacketMissingArgument(Exception):
    pass


def decode_args(types, data):
    args = []
    for argtype in types:
        if argtype == "I":
            elem, data = data[:4], data[4:]
            result = struct.unpack(">I", elem)[0]

        elif argtype == "S":
            length = struct.unpack(">H", data[:2])[0]
            result = data[2:2 + length].decode("utf-8", "ignore")
            data = data[2 + length:]

        elif argtype == "B":
            length = struct.unpack(">H", data[:2])[0]
            result = data[2:2 + length]
            data = data[2 + length:]

        elif argtype == "G":
            result, data = data[:5], data[5:]
            # Convert result (5 bytes) to a long.  Can't use
            # struct.unpack(">Q", "\x00"*3 + result), since we
            # can't rely on "long long" being available.
            high, low = struct.unpack(">BI", result)
            result = (high << 32) + low

        elif argtype == "i":
            length = struct.unpack(">H", data[:2])[0]
            result = struct.unpack(">%sI" % length, data[2:2 + 4 * length])
            data = data[2 + 4 * length:]

        elif argtype == "s":
            length = struct.unpack(">H", data[:2])[0]
            data = data[2:]
            result = []
            while length:
                slength = struct.unpack(">H", data[:2])[0]
                result.append(data[2:2 + slength].decode("utf-8"))
                data = data[2 + slength:]
                length -= 1

        else:
            raise UnknownArgumentType(argtype)

        args.append(result)

    return args


def encode_args(types, args):
    data = b""

    for argtype in types:
        if not args:
            raise PacketMissingArgument

        it = args[0]
        del args[0]

        if argtype == "I":
            data += struct.pack(">I", it)

        elif argtype == "S":
            encoded = it.encode("utf-8")
            data += struct.pack(">H", len(encoded))
            data += encoded

        elif argtype == "G":
            data += struct.pack(">BI", it >> 32, it & 0xffffffff)

        elif argtype == "s":
            data += struct.pack(">H", len(it))
            for it_elem in it:
                encoded = it_elem.encode("utf-8")
                data += struct.pack(">H", len(encoded))
                data += encoded

        else:
            raise UnknownArgumentType(argtype)

    return data


class Packet:
    pass
