#--
# Copyright (c) 2007 Robert S. Thau, Smartleaf, Inc.
# 
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.

class ApplicationController < ActionController::Base

  # Pick a unique cookie name to distinguish our session data from others'
  session :session_key => '_perm_code_session_id'

  before_filter :check_login
  filter_parameter_logging :password, :password_confirmation

  around_filter :db_log

  # Arrange for common boilerplate views to be included only ones
  # (via ardes Inherit Views plugin):

  inherit_views

  protected

  # Request filter for logging.  NB done as an around filter,
  # in part because after filters get skipped when an exception
  # is thrown...

  def db_log
    RequestLogEntry.logging( self, request, response ) do
      yield
    end
  end

  # Request filters for verifying that a user has logged in, 
  # and helpers for controllers that manage the process.
  #
  # (Right now, that's only the PasswordController, whose
  # tests exercise this code as well.  It's reasonable to
  # expect there will be, e.g., an SSO controller as well
  # at some point, in which case we'll need a few extra
  # session variables and a dispatch here to determine
  # what "logout" does in a particular session).

  def check_login               # :nodoc:

    current_user_id   = session[:current_user_id]
    user_of_record_id = session[:user_of_record_id]

    if user_of_record_id.nil?
      remember_diversion
      ensure_logged_out_and_redirect_to_login_page
      return
    end

    User.of_record = User.find user_of_record_id
    User.current   = User.find current_user_id

    if User.of_record.locked_out?
      ensure_logged_out_and_redirect_to_login_page
      return
    end

  end

  # This can be invoked by a before_filter that's hijacking
  # a request with a redirect, in order to remember the controller
  # and action being redirected from.  A subsequent call to
  # redirect_to_diverted_request will (attempt to) redirect back.
  # 
  # Note that these diversions don't stack (there can only be one
  # in effect at a time), and this mechanism is used by the 
  # password-based login machinery.

  def remember_diversion
    session[:diverted_controller] = controller_name
    session[:diverted_action]     = action_name
  end

  # Undo a diversion remembered by remember_diversion.

  def redirect_to_diverted_request

    controller = session[:diverted_controller] || 'stores'
    action     = session[:diverted_action]     || 'index'

    session[:diverted_controller] = nil
    session[:diverted_action]     = nil

    redirect_to :controller => controller, :action => action

  end

  # Returns true if we have remembered a diverted request.
  # See remember_diversion

  def have_diverted_request?
    !session[:diverted_controller].nil?
  end

  # Alter session state so that "user" will now be logged in.
  # The "user" becomes both the acting user and the user of record.

  def login_as( user )

    User.current   = user
    User.of_record = user
    session[:current_user_id]   = user.id
    session[:user_of_record_id] = user.id

  end

  # Alters session state so that the user is logged out;
  # also redirects to the login page.  (What else are you gonna do?)

  def ensure_logged_out_and_redirect_to_login_page
    session[:current_user_id]   = nil
    session[:user_of_record_id] = nil
    redirect_to :controller => 'password', :action => 'login'
  end

  # Alter session state so that we will now be acting as "other_user".
  # The user_of_record must have an permission to :act_as other_user,
  # or a PermissionFailure will be thrown.

  def assume_alias( other_user )

    if other_user != User.of_record
      User.acting_as_user_of_record do
        other_user.check_permission! :act_as
      end
    end

    User.current = other_user
    session[:current_user_id] = other_user.id

  end

  # Find (or create!) the current order for the given store
  # in this session.  Note that paid orders are *never* current...
  # this machinery is for orders still being constructed.

  def find_or_create_current_order_for( store )
    session[:orders] ||= {}
    order_id = session[:orders][store.id]
    order = Order.find( order_id ) unless order_id.nil?
    if order.nil? || order.paid?
      order = Order.create :store => store
      session[:orders][store.id] = order.id
    end
    return order
  end

  # Make a given order current... if not yet paid!

  def set_current_order( order )
    unless order.paid?
      session[:orders] ||= {}
      session[:orders][order.store.id] = order.id
    end
  end

end

