# frozen_string_literal: true

require_relative 'abstract_handler'
require_relative 'get_instance_field_handler'
require_relative '../../utils/command'
require_relative '../../utils/command_type'
require_relative '../../utils/runtime_name_ht'
require_relative '../../utils/types_handler'

module Hypertube
  module Core
    module Handler
      class PassByValueHandler < Hypertube::Core::Handler::AbstractHandler
        def initialize
          @required_parameters_count = 1
        end

        def process(command)
          validate(command, @required_parameters_count, self.class.name)
          instance = command.payload[0]
          project_value(instance)
        end

        private

        def project_value(value)
          return value if Hypertube::Utils::TypesHandler.primitive_or_none?(value)

          if value.is_a?(Array)
            projected_elements = value.map { |item| project_value(item) }
            return Hypertube::Utils::Command.new(Hypertube::Utils::RuntimeNameHt::RUBY, Hypertube::Utils::CommandType::ARRAY, projected_elements)
          end

          project_object(value)
        end

        def project_object(instance)
          type_name = instance.class.name
          property_names = instance.instance_variables.map(&:to_s)

          get_type_command = Hypertube::Utils::Command.new(Hypertube::Utils::RuntimeNameHt::RUBY, Hypertube::Utils::CommandType::GET_TYPE, type_name)
          current_chain = Hypertube::Utils::Command.new(Hypertube::Utils::RuntimeNameHt::RUBY, Hypertube::Utils::CommandType::CREATE_CLASS_INSTANCE, get_type_command)

          get_instance_field_handler = Hypertube::Core::Handler::GetInstanceFieldHandler.new
          property_names.each do |property_name|
            property_value = get_instance_field_handler.process(
              Hypertube::Utils::Command.new(Hypertube::Utils::RuntimeNameHt::RUBY, Hypertube::Utils::CommandType::GET_INSTANCE_FIELD, instance, property_name)
            )
            projected_value = project_value(property_value)
            current_chain = Hypertube::Utils::Command.new(
              Hypertube::Utils::RuntimeNameHt::RUBY,
              Hypertube::Utils::CommandType::SET_INSTANCE_FIELD,
              [current_chain, property_name, projected_value]
            )
          end

          current_chain
        end
      end
    end
  end
end
