module ActiveScaffold::DataStructures
  class ActionLink
    # provides a quick way to set any property of the object from a hash
    def initialize(action, options = {})
      # set defaults
      self.action = action
      self.label = action
      self.confirm = false
      self.type = :collection
      self.inline = true
      self.method = :get
      self.crud_type = :delete if [:destroy].include?(action.try(:to_sym))
      self.crud_type = :create if [:create, :new].include?(action.try(:to_sym))
      self.crud_type = :update if [:edit, :update].include?(action.try(:to_sym))
      self.crud_type ||= :read
      self.parameters = {}
      self.html_options = {}
      self.column = nil
      self.image = nil
      self.dynamic_parameters = nil
      self.weight = 0

      # apply quick properties
      options.each_pair do |k, v|
        setter = "#{k}="
        self.send(setter, v) if self.respond_to? setter
      end
    end

    # the weight for this link in the action links collection, it will be used to sort the collection
    attr_accessor :weight

    # the action-path for this link. what page to request? this is required!
    attr_accessor :action
    
    # the controller for this action link. if nil, the current controller should be assumed.
    attr_writer :controller

    def controller
      @controller = @controller.call if @controller.is_a?(Proc)
      @controller
    end

    def static_controller?
      !(@controller.is_a?(Proc) || (@controller == :polymorph))
    end

    # a hash of request parameters
    attr_accessor :parameters

    # a block for dynamic_parameters
    attr_accessor :dynamic_parameters

    # the RESTful method
    attr_accessor :method

    # what string to use to represent this action
    attr_writer :label
    def label
      @label.is_a?(Symbol) ? as_(@label) : @label
    end
    
    # image to use {:name => 'arrow.png', :size => '16x16'}
    attr_accessor :image

    # if the action requires confirmation
    def confirm=(value)
      @dhtml_confirm = nil if value
      @confirm = value
    end
    def confirm(label = '')
      @confirm.is_a?(String) ? @confirm : as_(@confirm, :label => label)
    end
    def confirm?
      !!@confirm
    end
    
    # if the action uses a DHTML based (i.e. 2-phase) confirmation
    attr_accessor :dhtml_confirm
    def dhtml_confirm=(value)
      @confirm = nil if value
      @dhtml_confirm = value
    end
    def dhtml_confirm?
      !!@dhtml_confirm
    end

    # what method to call on the controller to see if this action_link should be visible
    # if method return false, link will be disabled
    # note that this is only the UI part of the security. to prevent URL hax0rz, you also need security on requests (e.g. don't execute update method unless authorized).
    attr_writer :security_method
    def security_method
      @security_method || "#{self.action}_authorized?"
    end

    def security_method_set?
      !!@security_method
    end

    # enable it to refresh the parent row when the view is closed
    attr_accessor :refresh_on_close
    
    # what method to call on the controller to see if this action_link should be visible
    # if method return true, link won't be displayed
    attr_accessor :ignore_method
    
    # the crud type of the (eventual?) action. different than :method, because this crud action may not be imminent.
    # this is used to determine record-level authorization (e.g. record.authorized_for?(:crud_type => link.crud_type).
    # options are :create, :read, :update, and :delete
    attr_accessor :crud_type

    # an "inline" link is inserted into the existing page
    # exclusive with popup? and page?
    def inline=(val)
      @inline = (val == true)
      self.popup = self.page = false if @inline
    end
    def inline?; @inline end

    # a "popup" link displays in a separate (browser?) window. this will eventually take arguments.
    # exclusive with inline? and page?
    def popup=(val)
      @popup = (val == true)
      if @popup
        self.inline = self.page = false

        # the :method parameter doesn't mix with the :popup parameter
        # when/if we start using DHTML popups, we can bring :method back
        self.method = nil
      end
    end
    def popup?; @popup end

    # a "page" link displays by reloading the current page
    # exclusive with inline? and popup?
    def page=(val)
      @page = (val == true)
      if @page
        self.inline = self.popup = false

        # when :method is defined, ActionView adds an onclick to use a form ...
        # so it's best to just empty out :method whenever possible.
        # we only ever need to know @method = :get for things that default to POST.
        # the only things that default to POST are forms and ajax calls.
        # when @page = true, we don't use ajax.
        self.method = nil if method == :get
      end
    end
    def page?; @page end

    # where the result of this action should insert in the display.
    # for :type => :collection, supported values are:
    #   :top
    #   :bottom
    #   :replace (for updating the entire table)
    #   false (no attempt at positioning)
    # for :type => :member, supported values are:
    #   :before
    #   :replace
    #   :after
    #   false (no attempt at positioning)
    attr_writer :position
    def position
      return @position unless @position.nil? or @position == true
      return :replace if self.type == :member
      return :top if self.type == :collection
      raise "what should the default position be for #{self.type}?"
    end

    # what type of link this is. currently supported values are :collection and :member.
    attr_accessor :type

    # html options for the link
    attr_accessor :html_options
    
    # nested action_links are referencing a column
    attr_accessor :column
    
    # don't close the panel when another action link is open 
    attr_writer :keep_open
    def keep_open?
      @keep_open
    end
    
    # indicates that this a nested_link
    def nested_link?
      @column || (parameters && parameters[:named_scope])
    end
    
    def name_to_cache_link_url
      @name_to_cache_link_url ||= :"#{controller || 'self'}_#{action}#{'_' if parameters.present?}#{parameters.map{|k,v| "#{k}_#{v}"}.join('_')}_link_url"
    end
    
    
  end
end
