require 'eventmachine'
require 'madvertise/ext/config'
require 'mixlib/cli'
require 'servolux'
require 'socket'

require 'ganymed'
require 'ganymed/collector'
require 'ganymed/processor'
require 'ganymed/sampler'
require 'ganymed/version'

module Ganymed
  # @private
  class Master < ::Servolux::Server
    include Configuration::Helpers

    def initialize(cli)
      # load config file
      @default_config_file = File.join(LIB_DIR, 'ganymed/config.yml')
      @config_file = cli[:config_file]

      # store fqdn for later use
      config.fqdn = ::Socket.gethostbyname(::Socket.gethostname).first

      # initialize servolux
      super('ganymed',
            :interval => 1,
            :logger => log,
            :pid_file => cli[:pidfile])
    end

    def run
      unless EventMachine.reactor_running?
        @reactor.join if @reactor.is_a?(Thread)
        log.info("starting Ganymed reactor in #{Env.mode} mode")

        # configure eventmachine
        log.debug("EventMachine threadpool_size=#{config.eventmachine.threadpool_size}")
        EventMachine.threadpool_size = config.eventmachine.threadpool_size

        @reactor = Thread.new do
          begin
            EventMachine.run { setup_reactor }
          rescue Exception => exc
            log.exception(exc)
          end
        end
      end

      if config.profiling.gcprofiler
        puts GC::Profiler.report
      end
    end

    def setup_reactor
      Processor.new(config) if config.processor.enabled
      Sampler.new(config) if config.sampler.enabled
      Collector.new(config) if config.collectors.any?
    end

    def before_starting
      if config.profiling.perftools
        log.info("activating PerfTools CPU profiler")
        require 'perftools'
        PerfTools::CpuProfiler.start('profile')
      end

      if config.profiling.gcprofiler
        log.info("activating GC::Profiler")
        GC::Profiler.enable
      end

      if config.profiling.rubyprof
        require 'ruby-prof'
        log.info("activating RubyProf")
        RubyProf.start
      end
    end


    def after_stopping
      log.info("shutting down Ganymed reactor")
      EventMachine.stop_event_loop if EventMachine.reactor_running?
      @reactor.join if @reactor.is_a?(Thread)

      if config.profiling.perftools
        PerfTools::CpuProfiler.stop
      end

      if config.profiling.rubyprof
        result = RubyProf.stop
        result.eliminate_methods!([
          /Queue#pop/,
          /Queue#push/,
          /Mutex#synchronize/,
          /Mutex#sleep/,
        ])
        printer = RubyProf::MultiPrinter.new(result)
        printer.print(:path => "prof", :profile => "ganymed")
      end

      log.info("done shutting down. exiting ...")
    end

    class CLI
      include Mixlib::CLI

      option :config_file,
        :short => '-c CONFIG',
        :long => '--config CONFIG',
        :description => "The configuration file to use"

      option :pidfile,
        :short => '-p PIDFILE',
        :long => '--pidfile PIDFILE',
        :description => "The daemon pidfile",
        :default => "ganymed.pid"

      option :debug,
        :short => '-D',
        :long => '--debug',
        :description => "Enable debug output",
        :boolean => true

      option :daemonize,
        :short => '-d',
        :long => '--daemonize',
        :description => "Daemonize the server process",
        :boolean => true

      option :kill,
        :short => '-k',
        :long => '--kill',
        :description => "Kill the currently running daemon instance",
        :boolean => true

      option :environment,
        :short => '-e ENVIRONMENT',
        :long => '--environment ENVIRONMENT',
        :description => "Set the daemon environment",
        :default => "development",
        :proc => ->(value) { ENV[Env.key] = value }

      option :help,
        :short => '-h',
        :long => '--help',
        :description => "Show this message",
        :on => :tail,
        :boolean => true,
        :show_options => true,
        :exit => 0

      def self.parse_options
        new.tap do |cli|
          cli.parse_options
        end
      end
    end
  end
end