require 'forwardable'
  
module Glimmer
  module LibUI
    # GUI View objects that can be represented by PerfectShape objects
    module PerfectShaped
      extend Forwardable
      
      def_delegators :perfect_shape,
                     :min_x, :min_y, :max_x, :max_y, :center_point, :center_x, :center_y
                     
      # Returns if shape contains point on the inside when outline is false (default)
      # or if point is on the outline when outline is true
      # distance_tolerance is used when outline is true to enable a fuzz factor in
      # determining if a point lies on the outline (e.g. makes it easier to select
      # a shape by mouse)
      def contain?(*point, outline: false, distance_tolerance: 0)
        # TODO inverse transform point with `uiDrawMatrixTransformPoint` before checking containment
        perfect_shape&.contain?(*point, outline: outline, distance_tolerance: distance_tolerance)
      end
      
      # Returns if shape includes point on the inside when filled
      # or if shape includes point on the outline when stroked
      def include?(*point)
        if respond_to?(:fill) && fill.empty?
          # TODO check if distance_tolerance should be half the thickness in case it is checked against both sides of out and in
          contain?(*point, outline: true, distance_tolerance: ((stroke[:thickness] || 1) - 1))
        else
          contain?(*point)
        end
      end
      
      # Returns bounding box Array consisting of
      # [minx, miny, width, height]
      def bounding_box
        perfect_shape_bounding_box = perfect_shape&.bounding_box
        return if perfect_shape_bounding_box.nil?
        [
          perfect_shape_bounding_box.x,
          perfect_shape_bounding_box.y,
          perfect_shape_bounding_box.width,
          perfect_shape_bounding_box.height,
        ]
      end
      
      # moves by x delta and y delta. Classes must implement
      def move_by(x_delta, y_delta)
        # No Op
      end
      alias translate move_by
      
      def move(x, y)
        x_delta = x - perfect_shape.min_x
        y_delta = y - perfect_shape.min_y
        move_by(x_delta, y_delta)
      end
      
      # Returns PerfectShape object matching this shape to enable
      # executing computational geometry algorithms
      #
      # Including classes must implement
      def perfect_shape
        # No Op
      end
      
      # Returns PerfectShape object dependencies to determine if the PerfectShape
      # object changed or not for caching purposes.
      # Every shape/path implements this uniquely for its own PerfectShape attribute dependencies
      def perfect_shape_dependencies
        # No Op
      end
    end
  end
end
