require 'singleton'

module CanTango
  class Configuration
    class Engines
      autoload_modules :Permission, :Permit, :UserAc, :Cache, :Store, :Engine

      include Singleton
      include Enumerable

      # engine registry is a simple hash
      def register hash
        hash.each_pair do |name, engine_class|
          raise "Class must implement the CanTango Engine API. You can start by sublclassing CanTango::Engine" if !engine? engine_class
          raise "Name of engine must be a String or Symbol" if !name.kind_of_label?
          registered[name.to_sym] = engine_class
        end
      end

      # engine factories ?
      # :cache => Cantango::Ability::Cache
      def registered
        @registered ||= default_register
      end

      def unregister name
        @registered = {} if name == :all
        @registered.delete(name)
      end

      def default_register
        {:permit => CanTango::PermitEngine, :permission => CanTango::PermissionEngine, :user_ac => CanTango::UserAcEngine }
      end

      # defines the order of execution of engine in ability
      def execution_order= *names
        @execution_order = names.to_symbols.select {|name| available? name }
      end

      def execute_first name
        execution_order.insert(0, name)
      end

      def execute_last name
        execution_order.insert(-1, name)
      end

      def execute_before existing, name
        index = execution_order.index(existing) || 0
        execution_order.insert(index, name)
      end

      def execute_after existing, name
        index = execution_order.index(existing)
        index ? execution_order.insert(index +1, name) : execute_last(name)
      end

      def execution_order
        @execution_order ||= (self.class.default_available - [:cache])
      end

      def self.default_available
        [:cache, :permission , :permit, :user_ac]
      end

      def default_available
        self.class.default_available
      end

      def available
        (registered.keys + default_available).uniq
      end

      def available? name
        available.include? name.to_sym
      end

      def all state
        available.each {|engine| send(engine).set state }
      end

      def any? state
        available.any? {|engine| send(engine).send(:"#{state}?") if respond_to?(engine) }
      end

      def clear!
        each {|engine| engine.reset! }
        @registered = nil
        @execution_order = nil
      end

      def each
        available.each {|engine| yield send(engine) if respond_to?(engine) }
      end

      def active? name
        active.include? name.to_sym
      end

      def active
        available.select {|engine| send(engine).on? if respond_to?(engine) }
      end

      default_available.each do |engine|
        # def permission
        #   return Permission.instance
        # end
        class_eval %{
          def #{engine}
            CanTango::Configuration::Engines::#{engine.to_s.camelize}.instance
          end
        }
      end

      protected

      # does it implement the basic Engine API?
      def engine? engine_class
        [:execute!, :ability].all? {|meth| engine_class.instance_methods.include? meth }
      end
    end
  end
end


