# frozen_string_literal: true

# Copyright (c) 2002 - 2009 Timothy P. Hunter
# Copyright (c) 2009 -      RMagick contributors

module Magick
  class RVG
    # Content is simply an Array with a deep_copy method.
    # When unit-testing, it also has a deep_equal method.
    # @private
    class Content < Array
      def deep_copy(h = {})
        me = __id__
        copy = h[me]
        unless copy
          copy = self.class.new
          each do |c|
            copy << if c.nil?
                      nil
                    elsif c.respond_to?(:deep_copy)
                      c.deep_copy(h)
                    elsif c.respond_to?(:dup)
                      begin
                        c.dup
                      rescue StandardError
                        c
                      end
                    else
                      c
                    end
          end
          copy.freeze if frozen?
          h[me] = copy
        end
        copy
      end
    end # class Content

    # Define a collection of shapes, text, etc. that can be reused.
    # Group objects are _containers_. That is, styles and transforms defined
    # on the group are used by contained objects such as shapes, text, and
    # nested groups unless overridden by a nested container or the object itself.
    # Groups can be reused with the RVG::UseConstructors#use method.
    # Create groups within
    # containers with the RVG::StructureConstructors#g method.
    #
    # Example:
    #   # All elements in the group will be translated by 50 in the
    #   # x-direction and 10 in the y-direction.
    #   rvg.g.translate(50, 10).styles(:stroke=>'red',:fill=>'none') do |grp|
    #       # The line will be red.
    #       grp.line(10,10, 20,20)
    #       # The circle will be blue.
    #       grp.circle(10, 20, 20).styles(:stroke=>'blue')
    #   end
    class Group
      include Stylable
      include Transformable
      include Embellishable
      include Describable
      include Duplicatable

      def initialize
        super
        @content = Content.new
        yield(self) if block_given?
      end

      # @private
      def add_primitives(gc)
        gc.push
        add_transform_primitives(gc)
        add_style_primitives(gc)
        @content.each { |element| element.add_primitives(gc) }
        gc.pop
      end

      # Translate container according to #use arguments
      # @private
      def ref(x, y, _width, _height)
        translate(x, y) if x != 0 || y != 0
      end

      # Append an arbitrary object to the group's content. Called
      # by #use to insert a non-container object into a group.
      # @private
      def <<(obj)
        @content << obj
      end
    end # class Group

    # A Use object allows the re-use of RVG and RVG::Group
    # objects within a container. Create a Use object with the
    # RVG::UseConstructors#use method.
    class Use
      include Stylable
      include Transformable
      include Duplicatable

      # In a container, Use objects are created indirectly via the
      # RVG::UseConstructors#use method.
      # The +x+ and +y+ arguments
      # can be used to specify an additional translation for
      # the group. The +width+ and +height+ arguments specify
      # a width and height for referenced RVG objects.
      def initialize(element, x = 0, y = 0, width = nil, height = nil)
        super()

        # If the element is not a group, defs, symbol, or rvg,
        # wrap a group around it so it can get a transform and
        # possibly a new viewport.
        if element.respond_to?(:ref)
          @element = element.deep_copy
        else
          @element = Group.new
          @element << element.deep_copy
        end
        @element.ref(x, y, width, height)
      end

      # @private
      def add_primitives(gc)
        gc.push
        add_transform_primitives(gc)
        add_style_primitives(gc)
        @element.add_primitives(gc)
        gc.pop
      end
    end # class Use
  end # class RVG
end # module Magick
