require_relative 'type_serializer'
require_relative '../../utils/command'
require_relative '../../utils/command_type'
require_relative '../../utils/runtime_name_ht'
require_relative '../../utils/tcp_connection_data'
require_relative '../../utils/types_handler'
require_relative '../reference_cache/references_cache'

module Hypertube
  module Core
    module Protocol
      class CommandSerializer
        def self.serialize(root_command, connection_data = nil, runtime_version = 0)
          buffer = ''.dup
          buffer << [root_command.runtime_name, runtime_version].pack('C*')

          buffer << if connection_data
                      connection_data.serialize_connection_data.pack('C*')
                    else
                      [0, 0, 0, 0, 0, 0, 0].pack('C*')
                    end
          buffer << [Hypertube::Utils::RuntimeNameHt::RUBY, root_command.command_type].pack('C*')

          # Payload (recursive)
          serialize_recursively(root_command, buffer)

          buffer.bytes
        end

        private

        def self.serialize_recursively(command, buffer)
          command.payload.each do |item|
            if item.is_a?(Hypertube::Utils::Command)
              buffer << Hypertube::Core::Protocol::TypeSerializer.serialize_command(item).pack('C*')
              serialize_recursively(item, buffer)
            elsif Hypertube::Utils::TypesHandler.primitive_or_none?(item)
              buffer << Hypertube::Core::Protocol::TypeSerializer.serialize_primitive(item).pack('C*')
            else
              cached_reference = Hypertube::Core::ReferenceCache::ReferencesCache.instance.cache_reference(item)
              ref_command = Hypertube::Utils::Command.new(Hypertube::Utils::RuntimeNameHt::RUBY, Hypertube::Utils::CommandType::REFERENCE, [cached_reference])
              buffer << Hypertube::Core::Protocol::TypeSerializer.serialize_command(ref_command).pack('C*')
              serialize_recursively(ref_command, buffer)
            end
          end
        end
      end
    end
  end
end
