module Refinery
  module Engines; end;

  class Plugin

    def self.register(&block)
      plugin = self.new

      yield plugin

      raise "A plugin MUST have a name!: #{plugin.inspect}" if plugin.name.blank?

      # Set the root as Rails::Engine.called_from will always be
      #                 vendor/engines/refinery/lib/refinery
      new_called_from = begin
        # Remove the line number from backtraces making sure we don't leave anything behind
        call_stack = caller.map { |p| p.split(':')[0..-2].join(':') }
        File.dirname(call_stack.detect { |p| p !~ %r[railties[\w\-\.]*/lib/rails|rack[\w\-\.]*/lib/rack] })
      end

      klass = Class.new(Rails::Engine)
      klass.class_eval <<-RUBY
        def self.called_from; "#{new_called_from}"; end
      RUBY
      Object.const_set(plugin.class_name.to_sym, klass)
    end

    attr_accessor :name, :class_name, :controller, :directory, :url,
                  :version, :dashboard, :always_allow_access,
                  :menu_match, :hide_from_menu,
                  :pathname, :plugin_activity
    attr_reader   :description

    # Returns the class name of the plugin
    def class_name
      @class_name ||= name.camelize
    end

    # Returns the internationalized version of the title
    def title
      ::I18n.translate(['plugins', name, 'title'].join('.'))
    end

    # Returns the internationalized version of the description
    def description
      ::I18n.translate(['plugins', name, 'description'].join('.'))
    end

    # Retrieve information about how to access the latest activities of this plugin.
    def activity
      self.plugin_activity ||= []
    end

    # Stores information that can be used to retrieve the latest activities of this plugin
    def activity=(activities)
      [activities].flatten.each { |activity| add_activity(activity) }
    end

    # Returns true, if the user doesn't require plugin access
    def always_allow_access?
      @always_allow_access ||= false
    end

    # Special property to indicate that this plugin is the dashboard plugin.
    def dashboard?
      @dashboard ||= false
    end

    # Used to highlight the current tab in the admin interface
    def highlighted?(params)
      (params[:controller] =~ self.menu_match) or (self.dashboard? and params[:action] == 'error_404')
    end

    # Returns a RegExp that matches, if the current page is part of the plugin.
    def menu_match
      @menu_match ||= /(admin|refinery)\/#{self.name}$/
    end

    # Returns a hash that can be used to create a url that points to the administration part of the plugin.
    def url
      return @url if defined?(@url)

      if self.controller.present?
        @url = {:controller => "/admin/#{self.controller}"}
      elsif self.directory.present?
        @url = {:controller => "/admin/#{self.directory.split('/').pop}"}
      else
        @url = {:controller => "/admin/#{self.name}"}
      end
    end

  # Make this protected, so that only Plugin.register can use it.
  protected

    def add_activity(options)
      (self.plugin_activity ||= []) << Activity::new(options)
    end

    def initialize
      # save the pathname to where this plugin is using its lib directory which is standard now.
      depth = RUBY_VERSION >= "1.9.2" ? 4 : 3
      self.pathname = Pathname.new(caller(depth).first.match("(.*)#{File::SEPARATOR}lib")[1])
      Refinery::Plugins.registered << self # add me to the collection of registered plugins
    end
  end
end
