# BaseAuth

module Authorization

  DEFAULT_PERMISSION_DENIED_MESSAGE = 'You have no permissions to access this page.'

  def self.included( base )
    base.extend( ClassMethods )
    base.send( :include, InstanceMethods )
  end

  class PermissionDenied < Exception
    attr :message
    def initialize( message = nil )
      @message = message || DEFAULT_PERMISSION_DENIED_MESSAGE
    end
    def clean_message
      @message
    end
    def to_s
      @message
    end
  end
  
  module InstanceMethods
  
    def eval_authorization_conditions( conditions )
      exec = conditions[:exec]
      if exec
        case exec.class.name
          when 'Symbol':
            send( exec )
          when 'String':
            eval( exec )
          when 'Proc':
            exec.call
          else
            raise ArgumentError( ":exec doesn't accept values of class #{exec.class.name}" )
        end
      elsif conditions[:user]
        return false unless current_user
        if conditions[:user].is_a?( String )
          return current_user.instance_eval conditions[:user]
        end
        o = conditions[:object] || controller_name.singularize.to_sym
        object = o.is_a?( Symbol ) ? eval("@#{o}") : o
        
        methods = conditions[:user]
        methods = [ methods ] if not methods.is_a?( Array )
        
        for method in methods
          if current_user.respond_to?( method ) and
             current_user.method( method ).arity == 0
            return true if current_user.send( method )
          else
            return true if current_user.send( method, object )
          end
        end
        return false
      end
    end
    
    def allow?( conditions )
      eval_authorization_conditions( conditions )
    end
    
    def deny?( conditions )
      !eval_authorization_conditions( conditions )
    end
    
    def allow!( conditions )
      invoke_permission_denied_action( conditions ) unless allow?( conditions )
    end
    
    def deny!( conditions )
      invoke_permission_denied_action( conditions ) unless deny?( conditions )
    end
    
    def allow( conditions )
      if allow?( conditions )
        yield
      end
    end

    def deny( conditions )
      if deny?( conditions )
        yield
      end
    end

    def invoke_permission_denied_action( config )
      if config[:redirect_to]
        redirect_to config[:redirect_to]
      elsif config[:method]
        send( config[:method] )
      else
        raise Authorization::PermissionDenied.new( config[:message] )
      end
      false
    end
    
  end

  module ClassMethods
  
    def authorization_filter( action, attrs )
      config = { }
      config.update( attrs.pop ) if attrs.last.is_a?( Hash )
      
      opts = {}
      opts[:only] = config[:only]
      opts[:only] ||= attrs unless attrs.blank?
      opts[:except] = config[:except]
      
      if config[:if]
        
      end
      
      before_filter opts do |controller|
        if action == :allow
          controller.allow! config
        else
          controller.deny! config
        end
      end
    end
  
    def allow( *attrs )
      authorization_filter :allow, attrs
    end

    def deny( *attrs )
      authorization_filter :deny, attrs
    end
  end

end

ActionController::Base.send( :include, Authorization )
ActionController::Base.send( :helper_method, [ :allow, :deny ] )
