# frozen_string_literal: true

module Icalendar
  ##
  # Refines the  Icalendar::Calendar class by adding
  # the `scan` function to this class.
  #
  # @note [Refinement](https://ruby-doc.org/core-2.5.0/doc/syntax/refinements_rdoc.html)
  # is a _Ruby core feature_ since Ruby 2.0
  #
  module Scannable
    ##
    # Provides _mixin_ methods for the
    # Icalendar::Calendar class
    refine Icalendar::Calendar do # rubocop:disable Metrics/BlockLength
      using Icalendar::Schedulable
      ##
      # @param[date_time] begin_time
      # @param[date_time] closing_time
      # @param [Set] component_types a list of components that shall be retrieved these can be
      # - :events
      # - :todos
      # Note: `:journals` and `:freebusys` are currently not tested.
      #
      # @return [Array] all occurrences between begin_time and closing_time
      def scan(begin_time, closing_time, component_types = Set[:events])
        component_types = component_types.to_set
        result = []
        component_types.each do |component_type|
          result += _occurrences_between(_components(component_type), begin_time, closing_time)
        end
        result ||= [] # stop RubyMine to complain about uninitialized result.
        result.sort!
      end

      private def _components(component_type)
        # note: events(), todos(), journals(), freebusys() are attributes added
        # to Icalendar::Calendar by meta-programming.
        case component_type
        when :events then events
        when :todos then todos
          # :nocov:
        when :journals then journals
        when :freebusys then freebusys
          # :nocov:
        else
          raise ArgumentError, "Unknown Component type: `#{component_type}`."
        end
      end

      private def _occurrences_between(components, begin_time, closing_time)
        result = []
        components.each do |comp|
          occurrences = comp.schedule.occurrences_between(begin_time, closing_time)
          occurrences.each do |oc|
            new_oc = Icalendar::Rrule::Occurrence.new(self, comp, oc.start_time, oc.end_time)
            result << new_oc
          end
        end
        result
      end
    end
  end
end
