/*
* Copyright (c) 2006-2008 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Sensor server session
*
*/


#include "sensrvdefines.h"
#include "sensrvsession.h"
#include "sensrvserver.h"
#include "sensrvproxymanager.h"
#include "sensrvclientserver.h"
#include "sensrvtrace.h"
#include "sensrvmessage.h"
#include "sensrvchannelchangequeue.h"


// -----------------------------------------------------------------------------
// C++ constructor
// -----------------------------------------------------------------------------
//
CSensrvSession::CSensrvSession( CSensrvServer& aServer, 
                                CSensrvProxyManager& aProxyManager,
                                TSecureId aSecureId )
    : iServer(aServer),
      iProxyManager(aProxyManager),
      iSecureId(aSecureId)
    {
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::CSensrvSession()" ) ) );
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::CSensrvSession - return" )) );
    }

// -----------------------------------------------------------------------------
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CSensrvSession* CSensrvSession::NewL( CSensrvServer& aServer, 
                                      CSensrvProxyManager& aProxyManager,
                                      TSecureId aSecureId )
    {
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::NewL()" ) ) );

    CSensrvSession* self = new( ELeave ) CSensrvSession(aServer, aProxyManager, aSecureId);
    
    CleanupStack::PushL(self);
    
    self->ConstructL();
    
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::NewL - return 0x%x" ), self ) );
    
    CleanupStack::Pop(self);

    return self;
    }

// -----------------------------------------------------------------------------
// SensrvSession::ConstructL()
// -----------------------------------------------------------------------------
//
void CSensrvSession::ConstructL()
    {
    iProxyManager.AddSession();
    }

// ---------------------------------------------------------
// Destructor
// ---------------------------------------------------------
//
CSensrvSession::~CSensrvSession()
    {
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::~CSensrvSession()" ) ) );

    if (iAsyncChannelChangeMessage)
        {
        iProxyManager.RemoveChannelChangeListenerFromDynamicChannelSsys(
            iChannelChangeSearchParams, iAsyncChannelChangeMessage->GetMessage());
        }

    // Kill all ongoing transactions and channel listeners initiated by this session
    // Note: This is not actually necessary for sessions that are used by channel finder,
    // but doing special case for that is not cost effective.
    iProxyManager.SessionTerminated(this);
    
    // iServer and iProxyManager are not owned and therefore not cleaned

    delete iSyncMessage;
    delete iAsyncDataMessage;
    delete iAsyncPropertyMessage;
    delete iAsyncConditionMessage;
    delete iAsyncChannelChangeMessage;
    
    delete iChannelChangeQueue;

#ifdef MEMORY_TRACE_DEBUG
    // TRACE heap usage
    TInt heapSize = User::Heap().Size();
    TInt biggestBlock(0);
    TInt heapAvail = User::Heap().Available(biggestBlock);
    TInt used(heapSize-heapAvail);
    MEMORY_TRACE( ( _L( "#### Sensor Server, ~CSensrvSession - HEAP: Size: %d, Available: %d, Used: %d" ), heapSize, heapAvail, used ) );
#endif

    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::~CSensrvSession - return" ) ) );
    }

// -----------------------------------------------------------------------------
// Calls request handling functions. Also traps any leaves and signals client if
// error occurs.
// -----------------------------------------------------------------------------
//
void CSensrvSession::ServiceL( const RMessage2& aMessage )
    {
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::ServiceL(0x%x)" ), aMessage.Handle() ) );

#ifdef MEMORY_TRACE_DEBUG
    // TRACE heap usage
    TInt heapSize = User::Heap().Size();
    TInt biggestBlock(0);
    TInt heapAvail = User::Heap().Available(biggestBlock);
    TInt used(heapSize-heapAvail);
    MEMORY_TRACE( ( _L( "#### Sensor Server, ServiceL - HEAP: Size: %d, Available: %d, Used: %d" ), heapSize, heapAvail, used ) );
#endif

#ifdef API_TRACE_DEBUG
    switch ( aMessage.Function() )
        {
        case ESensrvSrvReqQueryChannels:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqQueryChannels ####" )) );
            break;
        case ESensrvSrvReqOpenChannel:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqOpenChannel ####" )) );
            break;
        case ESensrvSrvReqCloseChannel:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqCloseChannel ####" )) );
            break;
        case ESensrvSrvReqStartListening:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqStartListening ####" )) );
            break;
        case ESensrvSrvReqStopListening:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqStopListening ####" )) );
            break;
        case ESensrvSrvReqAsyncChannelData:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqAsyncChannelData ####" )) );
            break;
        case ESensrvSrvReqShutdownServer:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqShutdownServer ####" )) );
            break;
        case ESensrvSrvReqGetProperty:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqGetProperty ####" )) );
            break;
        case ESensrvSrvReqAsyncPropertyData:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqAsyncPropertyData ####" )) );
            break;
        case ESensrvSrvReqStopPropertyListening:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqStopPropertyListening ####" )) );
            break;
        case ESensrvSrvReqSetProperty:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqSetProperty ####" )) );
            break;
        case ESensrvSrvReqGetAllProperties:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqGetAllProperties ####" )) );
            break;
        case ESensrvSrvReqAddConditionSet:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqAddConditionSet ####" )) );
            break;
        case ESensrvSrvReqRemoveConditionSet:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqRemoveConditionSet ####" )) );
            break;
        case ESensrvSrvReqStartConditionListening:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqStartConditionListening ####" )) );
            break;
        case ESensrvSrvReqStopConditionListening:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqStopConditionListening ####" )) );
            break;
        case ESensrvSrvReqAsyncConditionMet:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqAsyncConditionMet ####" )) );
            break;
        case ESensrvSrvReqAsyncChannelChangeNotification:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqAsyncChannelChangeNotification ####" )) );
            break;
        case ESensrvSrvReqStopChannelChangeListening:
            API_TRACE( ( _L( "#### Sensor Server - Message from client #### ESensrvSrvReqStopChannelChangeListening ####" )) );
            break;
        default:
            break;
        }
#endif

    TInt err(KErrNone);
    CSensrvMessage* wrappedMsg = NULL;
    
    switch ( aMessage.Function() )
        {
        case ESensrvSrvReqQueryChannels:
        case ESensrvSrvReqOpenChannel:
        case ESensrvSrvReqCloseChannel:
        case ESensrvSrvReqStartListening:
        case ESensrvSrvReqStopListening:
        case ESensrvSrvReqGetProperty:
        case ESensrvSrvReqStopPropertyListening:
        case ESensrvSrvReqSetProperty:
        case ESensrvSrvReqGetAllProperties:
        case ESensrvSrvReqAddConditionSet:
        case ESensrvSrvReqRemoveConditionSet:
        case ESensrvSrvReqStartConditionListening:
        case ESensrvSrvReqStopConditionListening:
            {
            if (!iSyncMessage)
                {
                TRAP( err, iSyncMessage = CSensrvMessage::NewL() );
                }
            
            wrappedMsg = iSyncMessage;
            }
            break;
        
        case ESensrvSrvReqAsyncChannelData:
            {
            if (!iAsyncDataMessage)
                {
                TRAP( err, iAsyncDataMessage = CSensrvMessage::NewL() );
                }
            
            wrappedMsg = iAsyncDataMessage;
            }
            break;
        
        case ESensrvSrvReqAsyncPropertyData:
            {
            if (!iAsyncPropertyMessage)
                {
                TRAP( err, iAsyncPropertyMessage = CSensrvMessage::NewL() );
                }
            
            wrappedMsg = iAsyncPropertyMessage;
            }
            break;
        
        case ESensrvSrvReqAsyncConditionMet:
            {
            if (!iAsyncConditionMessage)
                {
                TRAP( err, iAsyncConditionMessage = CSensrvMessage::NewL() );
                }
            
            wrappedMsg = iAsyncConditionMessage;
            }
            break;
            
        case ESensrvSrvReqAsyncChannelChangeNotification:
            {
            // Create queue if it doesn't yet exist
            if (!iChannelChangeQueue)
                {
                TRAP(err, iChannelChangeQueue = CSensrvChannelChangeQueue::NewL());
                }

            if (err == KErrNone)
                {
                // Create async change notification message if it doesn't exist yet
                if (!iAsyncChannelChangeMessage)
                    {
                    TRAP( err, iAsyncChannelChangeMessage = CSensrvMessage::NewL() );
                    if (err != KErrNone)
                        {
                        ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::ServiceL - ERROR: Unable to create channel change notification message wrapper: %d" ), err ) );
                        }
                    }

                // Initialize wrapper and check if there are notifications waiting
                if (err == KErrNone)
                    {
                    iAsyncChannelChangeMessage->Initialize(aMessage);
                    
                    TSensrvChannelInfoPckgBuf searchParamsPckgBuf;
                    err = iAsyncChannelChangeMessage->Read( KSensrvAsyncChannelSearchParamsSlot, searchParamsPckgBuf );
                    if ( err == KErrNone )
                        {
                        iChannelChangeSearchParams = searchParamsPckgBuf();

                        iProxyManager.LoadDynamicChannelSsysIfNeeded(
                            iChannelChangeSearchParams, iAsyncChannelChangeMessage->GetMessage(), ETrue); // Ignore return value

                        CSensrvChannelChangeQueue::TChannelChangeNotification*
                            changeNotification = iChannelChangeQueue->First();
                        
                        if (changeNotification)
                            {
                            CompleteChangeNotificationMessage(changeNotification->iChannelInfo,
                                                              changeNotification->iChangeType);
                            iChannelChangeQueue->RemoveFirst();
                            }
                        }
                    else
                        {
                        ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::ServiceL - ERROR: Unable to read channel change search params: %d" ), err ) );
                        }
                    }
                }
            else
                {
                ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::ServiceL - ERROR: Unable to create channel change notification queue: %d" ), err ) );
                }

            if ( err != KErrNone )
                {
                aMessage.Complete( KErrGeneral );
                }
            }
            break;

        case ESensrvSrvReqStopChannelChangeListening:
            {
            if (iAsyncChannelChangeMessage)
                {
                iProxyManager.RemoveChannelChangeListenerFromDynamicChannelSsys(
                    iChannelChangeSearchParams, iAsyncChannelChangeMessage->GetMessage());
                }

            // Complete async message before deletion.
            if (iAsyncChannelChangeMessage && iAsyncChannelChangeMessage->Handle())
                {
                iAsyncChannelChangeMessage->Complete(KErrCancel);
                }
                
            // Delete channel notification queue and asynchronous notification message 
            delete iChannelChangeQueue;
            iChannelChangeQueue = NULL;
            delete iAsyncChannelChangeMessage;
            iAsyncChannelChangeMessage = NULL;
            
            // Complete stop message, since it won't be dispatched further.
            aMessage.Complete(KErrNone);
            }
            break;
            
        case ESensrvSrvReqShutdownServer:
            {
            // Test framework needs to be able to shutdown the server (checked in sensor server policy)
            API_TRACE( ( _L( "Sensor Server - CSensrvSession::ServiceL - Attempting shutdown from 0x%x" ), aMessage.SecureId().iId ) );
            iProxyManager.ShutdownServer(aMessage);
            }
            break;
        
        default:
            {
            ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::ServiceL - ERROR: Invalid function, panicing client" ) ) );
            aMessage.Panic(KSensrvPanicCategory, ESensrvPanicInvalidFunction);
            }
            break;
        }
    
    if (   aMessage.Function() == ESensrvSrvReqShutdownServer
        || aMessage.Function() == ESensrvSrvReqStopChannelChangeListening
        || aMessage.Function() == ESensrvSrvReqAsyncChannelChangeNotification)    
        {
        // Do nothing
        }
    else if ( wrappedMsg )
        {
        wrappedMsg->Initialize(aMessage);
        iProxyManager.DispatchMessage(*wrappedMsg);
        }
    else
        {
        aMessage.Complete(KErrNoMemory);
        }

#ifdef MEMORY_TRACE_DEBUG
    // TRACE heap usage
    heapSize = User::Heap().Size();
    heapAvail = User::Heap().Available(biggestBlock);
    TInt newUsed(heapSize-heapAvail);
    MEMORY_TRACE( ( _L( "#### Sensor Server, ServiceL - HEAP: Size: %d, Available: %d, Used: %d, Change in used: %d" ), heapSize, heapAvail, newUsed, newUsed - used ) );
#endif

    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::ServiceL - return" )) );
    }
    
// -----------------------------------------------------------------------------
// CSensrvSession::SecureId
// -----------------------------------------------------------------------------
//
TSecureId CSensrvSession::SecureId()
    {
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::SecureId()" ) ) );
            
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::SecureId - return 0x%x" ), iSecureId.iId ) );
    
    return iSecureId;
    }

// ---------------------------------------------------------------------------
// Adds channel change to queue if client is interested in such changes,
// unless async message is currently present, in which case will directly
// complete it without queueuing.
// ---------------------------------------------------------------------------
//
void CSensrvSession::NotifyChannelChange(const TSensrvResourceChannelInfo& aChangedChannel,
                                         TSensrvChannelChangeType aChangeType )
    {
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::NotifyChannelChange(aChangedChannel.iChannelId: %d, aChangeType: %d)"), aChangedChannel.iChannelId, aChangeType ) );

    // Client is interested if notification queue exists
    if (iChannelChangeQueue)    
        {
        // If async message is present, handle notification
        if (iAsyncChannelChangeMessage && iAsyncChannelChangeMessage->Handle())
            {
            CompleteChangeNotificationMessage(aChangedChannel, aChangeType);
            }
        else
            {
            // No async message currently, so queue notification
            iChannelChangeQueue->Append(aChangedChannel, aChangeType);
            }
        }
    
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::NotifyChannelChange - return" )) );
    }

// ---------------------------------------------------------
// Completes asynchronous change notification message
// ---------------------------------------------------------
//
void CSensrvSession::CompleteChangeNotificationMessage(const TSensrvResourceChannelInfo& aChangedChannel,
                                                       TSensrvChannelChangeType aChangeType )
    {
    COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::CompleteChangeNotificationMessage(aChangedChannel.iChannelId: %d, aChangeType: %d)" ), aChangedChannel.iChannelId, aChangeType ) );
    
    __ASSERT_DEBUG(iAsyncChannelChangeMessage, User::Panic(KSensrvPanicCategory, ESensrvPanicNullMessage));
    __ASSERT_DEBUG(iAsyncChannelChangeMessage->Handle(), User::Panic(KSensrvPanicCategory, ESensrvPanicNullMessage));
    
    // Check first if this channel change is interesting and allowed
    if ( aChangedChannel.IsMatch( iChannelChangeSearchParams, iAsyncChannelChangeMessage->GetMessage() ) )
        {
        // Change is interesting and allowed
        TInt err( KErrNone );
        TSensrvChannelInfoPckgBuf infoPckg( aChangedChannel );
        TInt space = iAsyncChannelChangeMessage->GetDesMaxLength( KSensrvAsyncChannelChangeInfoSlot );
        if ( space >= infoPckg.Length() )
            {
            err = iAsyncChannelChangeMessage->Write( KSensrvAsyncChannelChangeInfoSlot, infoPckg );
            if (err == KErrNone)
                {
                TSensrvTSensrvChannelChangeTypePckgBuf changeTypePckg( aChangeType );
    
                space = iAsyncChannelChangeMessage->GetDesMaxLength( KSensrvAsyncChannelChangeTypeSlot );
                
                if ( space >= changeTypePckg.Length() )
                    {
                    err = iAsyncChannelChangeMessage->Write( KSensrvAsyncChannelChangeTypeSlot, changeTypePckg );
    
                    if (err != KErrNone)
                        {
                        ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::CompleteChangeNotificationMessage - ERROR: Write to KSensrvAsyncChannelChangeTypeSlot failed: %d" ), err ) );
                        }
                    }
                else
                    {
                    ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::CompleteChangeNotificationMessage - ERROR: Not enough space to write to KSensrvAsyncChannelChangeTypeSlot" ) ) );
                    err = KErrOverflow;
                    }
                }
            else
                {
                ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::CompleteChangeNotificationMessage - ERROR: Write to KSensrvAsyncChannelChangeInfoSlot failed: %d" ), err ) );
                }
            }
        else  
            {
            ERROR_TRACE( ( _L( "Sensor Server - CSensrvSession::CompleteChangeNotificationMessage - ERROR: Not enough space to write to KSensrvAsyncChannelChangeInfoSlot" ) ) );
            err = KErrOverflow;
            }
        
        COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::CompleteChangeNotificationMessage - Completing: %d" ), err ) );
        
        // Complete message even if there is error. 
        iAsyncChannelChangeMessage->Complete(err);
        }
    else
        {
        // Change was not interesting or allowed
        COMPONENT_TRACE( ( _L( "Sensor Server - CSensrvSession::CompleteChangeNotificationMessage - Skipped" ) ) );
        }
    }
