require 'active_admin/router'
require 'active_admin/helpers/settings'

module ActiveAdmin
  class Application
    include Settings

    # Adds settings to both the Application and the Namespace instance
    # so that they can be configured independantly.
    def self.inheritable_setting(name, default)
      Namespace.setting name, nil
      setting name, default
    end

    # The default namespace to put controllers and routes inside. Set this
    # in config/initializers/active_admin.rb using:
    #
    #   config.default_namespace = :super_admin
    #
    setting :default_namespace, :admin

    # A hash of all the registered namespaces
    setting :namespaces, {}

    # Load paths for admin configurations. Add folders to this load path
    # to load up other resources for administration. External gems can
    # include their paths in this load path to provide active_admin UIs
    setting :load_paths, [File.expand_path('app/admin', Rails.root)]

    # The default number of resources to display on index pages
    inheritable_setting :default_per_page, 30

    # The title which gets displayed in the main layout
    inheritable_setting :site_title, ""

    # Set the site title link href (defaults to AA dashboard)
    inheritable_setting :site_title_link, ""

    # Set the site title image displayed in the main layout (has precendence over :site_title)
    inheritable_setting :site_title_image, ""

    # The view factory to use to generate all the view classes. Take
    # a look at ActiveAdmin::ViewFactory
    inheritable_setting :view_factory, ActiveAdmin::ViewFactory.new

    # The method to call in controllers to get the current user
    inheritable_setting :current_user_method, false

    # The method to call in the controllers to ensure that there
    # is a currently authenticated admin user
    inheritable_setting :authentication_method, false

    # The path to log user's out with. If set to a symbol, we assume
    # that it's a method to call which returns the path
    inheritable_setting :logout_link_path, :destroy_admin_user_session_path

    # The method to use when generating the link for user logout
    inheritable_setting :logout_link_method, :get

    # Active Admin makes educated guesses when displaying objects, this is
    # the list of methods it tries calling in order
    setting :display_name_methods, [ :display_name,
                                      :full_name,
                                      :name,
                                      :username,
                                      :login,
                                      :title,
                                      :email,
                                      :to_s ]

    # == Deprecated Settings

    # @deprecated The default sort order for index pages
    deprecated_setting :default_sort_order, 'id_desc'

    # DEPRECATED: This option is deprecated and will be removed. Use
    # the #allow_comments_in option instead
    attr_accessor :admin_notes

    include AssetRegistration

    # Event that gets triggered on load of Active Admin
    LoadEvent = 'active_admin.application.load'.freeze

    def setup!
      register_default_assets
    end

    def prepare!
      remove_active_admin_load_paths_from_rails_autoload_and_eager_load
      attach_reloader
      generate_stylesheets
    end

    # Registers a brand new configuration for the given resource.
    def register(resource, options = {}, &block)
      namespace_name = extract_namespace_name(options)
      namespace = find_or_create_namespace(namespace_name)
      namespace.register(resource, options, &block)
    end

    # Creates a namespace for the given name
    #
    # Yields the namespace if a block is given
    #
    # @returns [Namespace] the new or existing namespace
    def find_or_create_namespace(name)
      name ||= :root
      return namespaces[name] if namespaces[name]
      namespace = Namespace.new(self, name)
      namespaces[name] = namespace
      ActiveAdmin::Event.dispatch ActiveAdmin::Namespace::RegisterEvent, namespace
      yield(namespace) if block_given?
      namespace
    end

    alias_method :namespace, :find_or_create_namespace

    # Register a page
    #
    # @param name [String] The page name
    # @options [Hash] Accepts option :namespace.
    # @&block The registration block.
    #
    def register_page(name, options = {}, &block)
      namespace_name = extract_namespace_name(options)
      namespace = find_or_create_namespace(namespace_name)
      namespace.register_page(name, options, &block)
    end

    # Stores if everything has been loaded or we need to reload
    @@loaded = false

    # Returns true if all the configuration files have been loaded.
    def loaded?
      @@loaded
    end

    # Removes all the controllers that were defined by registering
    # resources for administration.
    #
    # We remove them, then load them on each request in development
    # to allow for changes without having to restart the server.
    def unload!
      namespaces.values.each{|namespace| namespace.unload! }
      @@loaded = false
    end

    # Loads all of the ruby files that are within the load path of
    # ActiveAdmin.load_paths. This should load all of the administration
    # UIs so that they are available for the router to proceed.
    #
    # The files are only loaded if we haven't already loaded all the files
    # and they aren't marked for re-loading. To mark the files for re-loading
    # you must first call ActiveAdmin.unload!
    def load!
      # No work to do if we've already loaded
      return false if loaded?

      # Load files
      files_in_load_path.each{|file| load file }

      # If no configurations, let's make sure you can still login
      load_default_namespace if namespaces.values.empty?

      # Load Menus
      namespaces.values.each{|namespace| namespace.load_menu! }

      # Dispatch an ActiveAdmin::Application::LoadEvent with the Application
      ActiveAdmin::Event.dispatch LoadEvent, self

      @@loaded = true
    end

    # Returns ALL the files to load from all the load paths
    def files_in_load_path
      load_paths.flatten.compact.uniq.collect{|path| Dir["#{path}/**/*.rb"] }.flatten
    end

    def router
      @router ||= Router.new(self)
    end

    def routes(rails_router)
      # Ensure that all the configurations (which define the routes)
      # are all loaded
      load!

      router.apply(rails_router)
    end

    def load_default_namespace
      find_or_create_namespace(default_namespace)
    end

    #
    # Add before, around and after filters to each registered resource.
    #
    # eg:
    #
    #   ActiveAdmin.before_filter :authenticate_admin!
    #
    def before_filter(*args, &block)
      ResourceController.before_filter(*args, &block)
    end

    def skip_before_filter(*args, &block)
      ResourceController.skip_before_filter(*args, &block)
    end

    def after_filter(*args, &block)
      ResourceController.after_filter(*args, &block)
    end

    def around_filter(*args, &block)
      ResourceController.around_filter(*args, &block)
    end

    # Helper method to add a dashboard section
    def dashboard_section(name, options = {}, &block)
      ActiveAdmin::Dashboards.add_section(name, options, &block)
    end

    private

    def register_default_assets
      register_stylesheet 'active_admin.css', :media => 'all'

      if !ActiveAdmin.use_asset_pipeline?
        register_javascript 'jquery.min.js'
        register_javascript 'jquery-ui.min.js'
        register_javascript 'jquery_ujs.js'
      end

      register_javascript 'active_admin.js'
    end

    def extract_namespace_name(options)
      options.has_key?(:namespace) ? options[:namespace] : default_namespace
    end

    # Since we're dealing with all our own file loading, we need
    # to remove our paths from the ActiveSupport autoload paths.
    # If not, file naming becomes very important and can cause clashes.
    def remove_active_admin_load_paths_from_rails_autoload_and_eager_load
      ActiveSupport::Dependencies.autoload_paths.reject!{|path| load_paths.include?(path) }
      # Don't eagerload our configs, we'll deal with them ourselves
      Rails.application.config.eager_load_paths = Rails.application.config.eager_load_paths.reject do |path|
        load_paths.include?(path)
      end
    end

    def attach_reloader
      ActiveAdmin::Reloader.build(Rails.application, self, Rails.version).attach!
    end

    def generate_stylesheets
      # This must be required after initialization
      require 'sass/plugin'
      require 'active_admin/sass/helpers'

      # Create our own asset pipeline in Rails 3.0
      if ActiveAdmin.use_asset_pipeline?
        # Add our mixins to the load path for SASS
        ::Sass::Engine::DEFAULT_OPTIONS[:load_paths] <<  File.expand_path("../../../app/assets/stylesheets", __FILE__)
      else
        require 'active_admin/sass/css_loader'
        ::Sass::Plugin.add_template_location(File.expand_path("../../../app/assets/stylesheets", __FILE__))
        ::Sass::Plugin.add_template_location(File.expand_path("../sass", __FILE__))
      end
    end
  end
end
