# -*- coding: UTF-8 -*-
#    This file is part of arena.
#   
#    arena is free software: you can redistribute it and/or modify it
#    under the terms of the GNU General Public License as published by the
#    Free Software Foundation; either version 3 of the License or, at your
#    option, any later version.
#
#    arena is distributed in the hope that it will be useful, but
#    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABLITY
#    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
#    License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with arena.  If not, see <http://www.gnu.org/licenses/>. 

## @file
#  This file customizes the ADataSession class to apply to arena engine 
#  instances.

from Plugins.Session.DataSessions import ADataSession, ASessionError
from Library.String.Utilities import Time
from Local.Configuration import Site
import random

## The variable used to track the name of the user
asUserName = "/user/username"
## The variable used to track the user agent
asUserAgent = "/user/user-agent"
## The session variable used to track the IPv4 address of the user
asIPV4SessionVariable = "/user/ip/v4"
## The session variable used to track the IPv6 address of the user
asIPV6SessionVariable = "/user/ip/v6"
## The variable used to track when the session was closed
asSessionLastClosed = "/session/last-closed"

## An instance of ADataSession customized for use with the various arena
#  engine instances.
class AnArenaSession(ADataSession):
    ## Constructor.
    #
    #  This routine expects that \p params will contain the following values:
    #  - \p ContentProvider -- The content provider for which the session is 
    #                          loaded
    #
    #  The following optional items may be included in \p params:
    #  - \p Mediator -- A reference to the content mediator is overseeing the
    #                   display of content to the client.  If this value is
    #                   omitted, or if it evaluates to \p None, the session
    #                   handler will attempt to retrieve a reference to the
    #                   mediator from the content provider.
    def __init__(Self, **params):
        params.update(category = Site.Sessions.category)
        
        ADataSession.__init__(Self, **params)
        
        if "Mediator" in Self._params:
            Self._Mediator = Self._params["Mediator"]
            del Self._params["Mediator"]
          
        if "ContentProvider" in Self._params:
            Self._ContentProvider = Self._params["ContentProvider"]
            del Self._params["ContentProvider"]
            
            if Self._Mediator is None:
                Self._Mediator = Self._ContentProvider.Mediator()
          
    ## Initializer
    def init(Self):
        result = ADataSession.init(Self)
        
        if result:
            # Establish private attributes
            # The mediator driving the channel display (and therefore the 
            # session)
            Self._Mediator = None
            # The channel being displayed
            Self._Channel = None
            # Establish public attributes
            # Set up the session ID generator
            Self.identifier = Site.Sessions.identifier()
            
        return result
     
    ## Delete expired sessions from the data source.
    def clean(Self):
        return Self._Source.delete(Self._category,
            criteria = [
                ("{field:expiration} <= {}", Time.now())
            ],
            immediate = True
        )
        
    ## Start a new session or continue an existing one, if a session ID
    #  is specified.
    def open(Self):
        result = -1
        
        # Clean old sessions, if indicated
        if random.randint(1, Site.Sessions.cleanup) == 1:
            Self.clean()
        
        # Get the session id
        sid = None
        
        # See if the session ID was provided by a cookie...
        if (Site.Sessions.inCookie) and \
          (Site.Sessions.cookieName in Self._Mediator.Cookie):
            sid = Self._Mediator.Cookie[Site.Sessions.cookieName].value
        
        if sid is not None:
            # Try to open an existing session
            if ADataSession.open(Self, Site.Sessions.visitorLength, sid) >= 0:
                # Renew the session, if we can
                if Self.hasExpired() == False:
                    if Self.isAnonymous() == False:
                        Self._maxLength = Site.Sessions.userLength
                    
                    if Site.Sessions.userRenew == True:
                        Self.renew()
                            
                    result = 0
                    
                # Unable to renew the session, so we start a new one
                else:
                   result = ADataSession.open(Self, Site.Sessions.visitorLength, 
                     None)
                    
            # Failed to open the session, so start a new one
            else:
                result = ADataSession.open(Self, Site.Sessions.visitorLength,
                  None)
                  
        else:
            # Create a new session
            result = ADataSession.open(Self, Site.Sessions.visitorLength, None)
            
        # Store the session ID in the client's cookie 
        if Site.Sessions.inCookie:   
            Self._Mediator.Cookie[Site.Sessions.cookieName] = Self._id
            # Update cookie expiration based on session expiration
            Self._Mediator.Cookie[Site.Sessions.cookieName]["expires"] = \
              Self.expirationTimeStamp()
            Self._Mediator.Cookie[Site.Sessions.cookieName]["path"] = "/"
        
        # Store the user name as a session variable
        if (asUserName not in Self) or (len(Self[asUserName]) == 0):
            Self[asUserName] = Site.Users.anonymous
            
        # Store the user agent as a session variable
        Self[asUserAgent] = Self._Mediator.Request.HTTP.userAgent
        
        # Update the user IP, if indicated by site configuration
        if Site.Users.logIP:
            Self[asIPV4SessionVariable] = \
                Self._Mediator.Request.Remote.address
          
        return result
        
    ## Close the session
    def close(Self):
        Self._closed = Time.now()
        Self[asSessionLastClosed] = Self._closed
        Self.flush()

    ## Properties --------------------------------------------------------------
    ## Determine whether the session is for an anonymous user or a registered 
    #  user, based on the "user-name" session variable.
    def isAnonymous(Self):
        result = True
        
        if asUserName in Self:
            if len(Self[asUserName]) > 0:
                result = (Self[asUserName] == Site.Users.anonymous)
            
        return result  
        
    ## Retrieve or set the name of the user, as specified by the session
    def userName(Self, *userName):
        if len(userName) == 0:
            return Self[asUserName]
            
        Self[asUserName] = userName[0]
        
    ## Retrieve the mediator driving the content provider (and therefore the
    #  session)
    def Mediator(Self):
        return Self._Mediator
        
    ## Retrieve the content provider
    def ContentProvider(Self):
        return Self._ContentProvider
        
