import io
from hypertube.core.protocol.TypeSerializer import TypeSerializer
from hypertube.core.referenceCache.ReferencesCache import ReferencesCache
from hypertube.utils.Command import Command
from hypertube.utils.CommandType import CommandType
from hypertube.utils.RuntimeName import RuntimeName
from hypertube.utils.TypesHandler import TypesHandler
from hypertube.utils.connectionData.IConnectionData import IConnectionData

class CommandSerializer:
    @staticmethod
    def serialize(root_command: Command, connection_data: IConnectionData, runtime_version=0):
        ms = io.BytesIO()

        ms.write(bytes([root_command.runtime_name.value, runtime_version]))

        if connection_data is not None:
            ms.write(bytes(connection_data.serialize_connection_data()))
        else:
            ms.write(bytes([0, 0, 0, 0, 0, 0, 0]))

        ms.write(bytes([RuntimeName.python.value, root_command.command_type.value]))

        CommandSerializer.serialize_recursively(root_command, ms)
        return ms.getvalue()

    @staticmethod
    def serialize_recursively(command: Command, ms: io.BytesIO):
        for item in command.get_payload():
            if isinstance(item, Command):
                ms.write(bytes(TypeSerializer.serialize_command(item)))
                CommandSerializer.serialize_recursively(item, ms)
            elif TypesHandler.is_primitive_or_none(item):
                ms.write(bytes(TypeSerializer.serialize_primitive(item)))
            else:
                cached_reference = ReferencesCache().cache_reference(item)
                ref_command = Command(RuntimeName.python, CommandType.Reference, [cached_reference])
                ms.write(bytes(TypeSerializer.serialize_command(ref_command)))
                CommandSerializer.serialize_recursively(ref_command, ms)