# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: true

module Contrast
  module Agent
    module Assess
      module Policy
        module Propagator
          # Propagation that results in all the tags of the source being
          # applied to the beginning of the target. The target's preexisting
          # tags are shifted to account for this.
          class Prepend < Contrast::Agent::Assess::Policy::Propagator::Base
            class << self
              # For the source, prepend its tags to the target. It's basically the
              # opposite of append. :-P
              def propagate propagation_node, preshift, target
                return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))

                sources = propagation_node.sources
                # source1 is the copy of the thing being prepended to
                source1 = find_source(sources[0], preshift)

                # source2 is the copy of the thing being prepended
                source2 = sources[1] ? find_source(sources[1], preshift) : source1

                original_start_index = target.rindex(source1) || 0
                # if the object and the return are the same length just copy the
                # tags from the object(since nothing from args was added to
                # return)
                if source1.length == target.length
                  properties.copy_from(source1, target, 0, propagation_node.untags)
                else
                  # find original in the target, copy tags to the new position in target
                  properties.copy_from(source1, target, original_start_index, propagation_node.untags)
                  # then we add the tags from the thing prepended to the start
                  # of the target
                  start = 0
                  while start < original_start_index
                    properties.copy_from(source2, target, start, propagation_node.untags)
                    start += source2.length
                    next unless start > original_start_index

                    properties.tags_at(start - source2.length).each do |tag|
                      tag.update_end(original_start_index)
                    end
                  end
                end
                # and finally merge the tags if any overlap.
                properties.cleanup_tags
              end
            end
          end
        end
      end
    end
  end
end