module Celluloid
  extend Forwardable
  def_delegators :actor_system, :[], :[]=

  class Actor
    class System
      extend Forwardable
      def_delegators :@registry, :[], :get, :[]=, :set, :delete

      ROOT_SERVICES = begin
        root_services = [
          {
            as: :notifications_fanout,
            type: Celluloid::Notifications::Fanout,
          },
          {
            as: :incident_reporter,
            type: Celluloid::IncidentReporter,
            args: [STDERR],
          },
          {
            as: :public_services,
            type: Celluloid::Supervision::Service::Public,
            accessors: [:services],
            supervise: [],
          },
        ]
        if $CELLULOID_MANAGED
          root_services << {
            as: :actor_manager,
            type: Celluloid::Actor::Manager,
            accessors: [:manager],
          }
        end
        root_services
      end

      attr_reader :registry, :group

      # the root of the supervisor tree is established at supervision/root

      def root_services
        @tree
      end

      def root_configuration
        @root
      end

      def initialize
        @tree = nil
        @group = Celluloid.group_class.new
        @registry = Internals::Registry.new
        @root = ROOT_SERVICES
      end

      # Launch default services
      def start
        within do
          @root = Supervision::Service::Root.define
          @tree = root_configuration.deploy
          # de root_services[:group_manager].manage! @group
        end
        true
      end

      def within
        old = Thread.current[:celluloid_actor_system]
        Thread.current[:celluloid_actor_system] = self
        yield
      ensure
        Thread.current[:celluloid_actor_system] = old
      end

      def get_thread
        @group.get do
          Thread.current[:celluloid_actor_system] = self
          yield
        end
      end

      def stack_dump
        Internals::Stack::Dump.new(@group)
      end

      def stack_summary
        Internals::Stack::Summary.new(@group)
      end

      def registered
        @registry.names
      end

      def clear_registry
        @registry.clear
      end

      def running
        actors = []
        @group.each do |t|
          next unless t.role == :actor
          actor = t.actor

          # NOTE - these are in separate statements, since on JRuby t.actor may
          # become nil befor .behavior_proxy() is called
          next unless actor
          next unless actor.respond_to?(:behavior_proxy)
          proxy = actor.behavior_proxy
          actors << proxy
        end
        actors
      end

      def running?
        @group.active?
      end

      # Shut down all running actors
      def shutdown
        actors = running
        Timeout.timeout(shutdown_timeout) do
          Internals::Logger.debug "Terminating #{actors.size} #{(actors.size > 1) ? 'actors' : 'actor'}..." if actors.size > 0

          # Actors cannot self-terminate, you must do it for them
          actors.each do |actor|
            begin
              actor.terminate!
            rescue DeadActorError
            end
          end

          actors.each do |actor|
            begin
              Actor.join(actor)
            rescue DeadActorError
            end
          end
        end
      rescue Timeout::Error
        Internals::Logger.error("Couldn't cleanly terminate all actors in #{shutdown_timeout} seconds!")
        unless RUBY_PLATFORM == "java" || RUBY_ENGINE == "rbx"
          actors.each do |actor|
            begin
              Actor.kill(actor)
            rescue DeadActorError, MailboxDead
            end
          end
        end
      ensure
        @group.shutdown
        clear_registry
      end

      def assert_inactive
        @group.assert_inactive
      end

      def shutdown_timeout
        Celluloid.shutdown_timeout
      end
    end
  end
end
