# frozen_string_literal: true

# This module includes methods for storing segments inside segments.
# has_children(child_types) defines three methods dynamically.

module HL7::Message::SegmentListStorage
  attr_reader :child_types

  def add_child_type(child_type)
    if defined?(@child_types)
      @child_types << child_type.to_sym
    else
      has_children [child_type.to_sym]
    end
  end

private

  # allows a segment to store other segment objects
  # used to handle associated lists like one OBR to many OBX segments
  def has_children(child_types)
    @child_types = child_types

    define_method_child_types
    define_method_children
    define_method_accepts
  end

  def define_method_child_types
    define_method(:child_types) do
      self.class.child_types
    end
  end

  def define_method_accepts
    class_eval do
      define_method(:accepts?) do |t|
        t = t.to_sym if t.respond_to?(:to_sym)
        !!child_types.index(t)
      end
    end
  end

  def define_method_children
    class_eval do
      define_method(:children) do
        unless defined?(@my_children)
          p = self
          @my_children ||= []
          @my_children.instance_eval do
            @parental = p
            alias :old_append :<<

            def <<(value)
              # do nothing if value is nil
              return unless value

              # make sure it's an array
              value = [value].flatten
              value.map {|item| append(item) }
            end

            def append(value)
              unless value.is_a?(HL7::Message::Segment)
                raise HL7::Exception, "attempting to append non-segment to a segment list"
              end

              value.segment_parent = @parental
              k = @parental
              k = k.segment_parent while k&.segment_parent && !k.segment_parent.is_a?(HL7::Message)
              k.segment_parent << value if k&.segment_parent
              old_append(value)
            end
          end
        end
        @my_children
      end
    end
  end
end