# Add necessary Rails path
$LOAD_PATH.unshift "#{RADIANT_ROOT}/vendor/rails/railties/lib"

require 'initializer'
require 'radiant/admin_ui'
require 'radiant/extension_loader'
require 'radiant/extension_locator'
require 'radiant/gem_locator'

module Radiant
  autoload :Cache, 'radiant/cache'
  
  class << self
    # Radiant.config returns the Radiant::Config eigenclass object, so it can be used wherever you would use Radiant::Config.
    #
    #   Radiant.config['site.title']
    #   Radiant.config['site.url'] = 'example.com'
    #
    # but it will also yield itself to a block:
    #
    #   Radiant.config do |config|
    #     config.define 'something', default => 'something'
    #     config['other.thing'] = 'nothing'
    #   end
    #    
    def config  # method must be defined before any initializers run
      yield Radiant::Config if block_given?
      Radiant::Config
    end
  end
  
  # NB. Radiant::Configuration (aka Rails.configuration) is an extension-aware subclass of Rails::Configuration 
  #     Radiant::Config (aka Radiant.config) is the application-configuration model class
  
  class Configuration < Rails::Configuration
    attr_accessor :extension_paths, :ignored_extensions
    attr_writer :extensions
    attr_accessor :view_paths
    attr_accessor :extension_dependencies

    def initialize
      self.view_paths = []
      self.extension_paths = default_extension_paths
      self.extension_dependencies = []
      super
    end

    def default_extension_paths
      env = ENV["RAILS_ENV"] || RAILS_ENV
      paths = [RAILS_ROOT + '/vendor/extensions', RADIANT_ROOT + '/vendor/extensions'].uniq
      # There's no other way it will work, config/environments/test.rb loads too late
      # TODO: Should figure out how to include this extension path only for the tests that need it
      paths.unshift(RADIANT_ROOT + "/test/fixtures/extensions") if env == "test"
      paths
    end

    def default_plugin_locators
      locators = []
      locators << Radiant::ExtensionLocator if defined? Gem
      locators << Radiant::GemLocator if defined? Gem
      locators << Rails::Plugin::FileSystemLocator
    end
    
    def extensions
      @extensions ||= all_available_extensions
    end

    def extensions_in_order
      return @extensions_in_order if @extensions_in_order
      @extensions_in_order = (if extensions.include?(:all)
        before_all = extensions.collect {|e| e if extensions.index(e).to_i < extensions.index(:all).to_i }.compact
        after_all = extensions.collect {|e| e if extensions.index(e).to_i > extensions.index(:all).to_i }.compact
        replacing_all_symbol = all_available_extensions - ((all_available_extensions & before_all) + (all_available_extensions & after_all))
        before_all + replacing_all_symbol + after_all
      else
        extensions
      end) - ignored_extensions
    end
      
    def ignore_extensions(array)
      self.ignored_extensions ||= []
      self.ignored_extensions |= array
    end
    
    def all_available_extensions
      # load vendorized extensions by inspecting load path(s)
      all = extension_paths.map do |path|
        Dir["#{path}/*"].select {|f| File.directory?(f) }
      end
      # load any gem that follows extension rules
      gems.inject(all) do |available,gem|
        available << gem.specification.full_gem_path if gem.specification and
          gem.specification.full_gem_path[/radiant-.*-extension-[\d\.]+$/]
        available
      end
      # strip version info to glean proper extension names
      all.flatten.map {|f| File.basename(f).gsub(/^radiant-|-extension-[\d\.]+$/, '') }.sort.map {|e| e.to_sym }
    end
    
    def admin
      AdminUI.instance
    end

    def extension(ext)
      ::ActiveSupport::Deprecation.warn("Extension dependencies have been deprecated. Extensions may be packaged as gems and use the Gem spec to declare dependencies.", caller)
      @extension_dependencies << ext unless @extension_dependencies.include?(ext)
    end

    def gem(name, options = {})
      super
      if gems.last.name =~ /^radiant-(.*)-extension$/ && extension_symbol = $1.intern
        @extensions ||= []
        if (@extensions & [extension_symbol, :all]).empty?
          extensions << extension_symbol
        end
      end
    end

    def check_extension_dependencies
      unloaded_extensions = []
      @extension_dependencies.each do |ext|
        extension = ext.camelcase + 'Extension'
        begin
          extension_class = extension.constantize
          unloaded_extensions << extension unless defined?(extension_class) && (extension_class.active?)
        rescue NameError
          unloaded_extensions << extension
        end
      end
      if unloaded_extensions.any?
        abort <<-end_error
Missing these required extensions:
#{unloaded_extensions}
end_error
      else
        return true
      end
    end

    private

      def library_directories
        require "#{RADIANT_ROOT}/lib/radiant/gem_dependency_fix"
        libs = %W{ 
          #{RADIANT_ROOT}/vendor/radius/lib
          #{RADIANT_ROOT}/vendor/highline/lib
          #{RADIANT_ROOT}/vendor/rack-cache/lib
        }
        begin
          Object.send :gem, 'RedCloth', ">=4.0.0"
          require 'redcloth'
        rescue LoadError, Gem::LoadError
          # If the gem is not available, use the packaged version
          libs << "#{RADIANT_ROOT}/vendor/redcloth/lib"
          after_initialize do
            warn "RedCloth > 4.0 not found.  Falling back to RedCloth 3.0.4 (2005-09-15).  You should run `gem install RedCloth`."
            require 'redcloth'
          end
        end
        libs
      end

      def framework_root_path
        RADIANT_ROOT + '/vendor/rails'
      end

      # Provide the load paths for the Radiant installation
      def default_autoload_paths
        paths = ["#{RADIANT_ROOT}/test/mocks/#{environment}"]

        # Add the app's controller directory
        paths.concat(Dir["#{RADIANT_ROOT}/app/controllers/"])

        # Followed by the standard includes.
        paths.concat %w(
          app
          app/metal
          app/models
          app/controllers
          app/helpers
          config
          lib
          vendor
        ).map { |dir| "#{RADIANT_ROOT}/#{dir}" }.select { |dir| File.directory?(dir) }

        paths.concat builtin_directories
        paths.concat library_directories
      end

      def default_plugin_paths
        [
          "#{RAILS_ROOT}/vendor/plugins",
          "#{RADIANT_ROOT}/lib/plugins",
          "#{RADIANT_ROOT}/vendor/plugins"
        ]
      end

      def default_view_path
        File.join(RADIANT_ROOT, 'app', 'views')
      end

      def default_controller_paths
        [File.join(RADIANT_ROOT, 'app', 'controllers')]
      end
  end

  class Initializer < Rails::Initializer
    def self.run(command = :process, configuration = Configuration.new)
      Rails.configuration = configuration
      super
    end

    def set_autoload_paths
      extension_loader.add_extension_paths
      super
    end
    
    # override Rails initializer to insert extension metals
    def initialize_metal
      Rails::Rack::Metal.requested_metals = configuration.metals
      Rails::Rack::Metal.metal_paths = ["#{RADIANT_ROOT}/app/metal"] # reset Rails default to RADIANT_ROOT
      Rails::Rack::Metal.metal_paths += plugin_loader.engine_metal_paths
      Rails::Rack::Metal.metal_paths += extension_loader.metal_paths
    
      configuration.middleware.insert_before(
        :"ActionController::ParamsParser",
        Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?)
    end
    
    def initialize_i18n
      extension_loader.add_locale_paths
      radiant_locale_paths = Dir[File.join(RADIANT_ROOT, 'config', 'locales', '*.{rb,yml}')]
      configuration.i18n.load_path = radiant_locale_paths + extension_loader.configuration.i18n.load_path
      super
    end

    def add_plugin_load_paths
      # checks for plugins within extensions:
      extension_loader.add_plugin_paths
      super
      ActiveSupport::Dependencies.autoload_once_paths -= extension_loader.extension_load_paths
    end

    def load_plugins
      super
      extension_loader.load_extensions
      add_gem_load_paths
      load_gems
      check_gem_dependencies
    end
    
    def load_application_initializers
      load_radiant_initializers
      super
      extension_loader.load_extension_initalizers
    end

    def load_radiant_initializers
      unless RADIANT_ROOT == RAILS_ROOT   ## in that case the initializers will be run during normal rails startup
        Dir["#{RADIANT_ROOT}/config/initializers/**/*.rb"].sort.each do |initializer|
          load(initializer)
        end
      end
    end

    def after_initialize
      super
      extension_loader.activate_extensions
      configuration.check_extension_dependencies
    end
    
    def initialize_default_admin_tabs
      admin.nav.clear
      admin.load_default_nav
    end

    def initialize_framework_views
      view_paths = [].tap do |arr|
        # Add the singular view path if it's not in the list
        arr << configuration.view_path if !configuration.view_paths.include?(configuration.view_path)
        # Add the default view paths
        arr.concat configuration.view_paths
        # Add the extension view paths
        arr.concat extension_loader.view_paths
        # Reverse the list so extensions come first
        arr.reverse!
      end
      if configuration.frameworks.include?(:action_mailer) || defined?(ActionMailer::Base)
        # This happens before the plugins are loaded so we must load it manually
        unless ActionMailer::Base.respond_to? :view_paths
          require "#{RADIANT_ROOT}/lib/plugins/extension_patches/lib/mailer_view_paths_extension"
        end
        ActionMailer::Base.view_paths = ActionView::Base.process_view_paths(view_paths)
      end
      if configuration.frameworks.include?(:action_controller) || defined?(ActionController::Base)
        view_paths.each do |vp|
          unless ActionController::Base.view_paths.include?(vp)
            ActionController::Base.prepend_view_path vp
          end
        end
      end
    end

    def initialize_routing
      extension_loader.add_controller_paths
      super
    end
    
    def admin
      configuration.admin
    end

    def extension_loader
      ExtensionLoader.instance {|l| l.initializer = self }
    end

  end

end
