require 'eventmachine'

require 'ganymed'
require 'ganymed/client'

module Ganymed

  ##
  # The Collector polls various system-level metrics that are not pushed to the
  # {Processor} automatically.
  #
  class Collector
    # The configuration object.
    attr_reader :config

    # The {Client} object.
    attr_reader :client

    # Create a new collector instance and initialize all configured collectors.
    #
    # @param [Configuration] config  The configuration object.
    def initialize(config)
      @config = config
      @client = Client.new(:processor => @config.collector.processor,
                           :sampler => @config.collector.sampler)

      @config.collectors.each do |collector|
        log.info("initializing collector #{collector.klass.demodulize}")
        collector.klass.constantize.new(collector, self).run
      end
    end

    ##
    # A base class for collectors.
    #
    class Base

      # Create a new data collector instance.
      #
      # @param [Configuration] config  The configuration object.
      # @param [Collector] collector  The collector instance.
      def initialize(config, collector)
        @config, @collector = config, collector
        @processor = @collector.client.processor
        @sampler = @collector.client.sampler
      end

      # Start the EventMachine timer to collect metrics at the specified
      # interval.
      def run
        timer do
          begin
            collect!
          rescue Exception => e
            log.exception(e)
          end
        end
      end

      # @private
      def timer
        if interval
          EM.add_periodic_timer(interval) do
            EM.defer { yield }
          end
        else
          yield
        end
      end

      # The interval used to collect metrics. Either taken from the
      # configuration file or overriden by subclasses.
      def interval
        @config.interval.tap{} or @collector.config.resolution
      end

      # This method must be implemented by subclasses to collect the desired
      # metrics. This function is called every #interval seconds after the
      # timer has been initialized by the #run method.
      def collect!
        raise NotImplementedError
      end
    end
  end
end

Dir[File.join(Ganymed::LIB_DIR, 'ganymed/collector/*.rb')].each do |f|
  require f
end