/*
* Copyright (c) 2002-2004 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:  Alert queue and handler
*
*/

#include <nsmldebug.h>

#include "NSmlAlertQueue.h"
#include "nsmlsosserverdefs.h"

//Fix to Remove the Bad Compiler Warnings
#ifndef __WINS__
// This lowers the unnecessary compiler warning (armv5) to remark.
// "Warning:  #174-D: expression has no effect..." is caused by 
// DBG_ARGS8 macro in no-debug builds.
#pragma diag_remark 174
#endif

// ============================ MEMBER FUNCTIONS ===============================

// ---------------------------------------------------------
// CNSmlAlertHandler::NewL(MNSmlMessageHandler* aMsgHandler)
// Two phase constructor
// ---------------------------------------------------------
//
CNSmlAlertHandler* CNSmlAlertHandler::NewL( MNSmlMessageHandler* aMsgHandler )
	{
	CNSmlAlertHandler* self = new (ELeave) CNSmlAlertHandler;
	CleanupStack::PushL( self );
	self->ConstructL( aMsgHandler );
	CleanupStack::Pop();
	return self;
	}

// ---------------------------------------------------------
// CNSmlAlertHandler::CNSmlAlertHandler()
// Constructor
// ---------------------------------------------------------
CNSmlAlertHandler::CNSmlAlertHandler()
: CActive(0), iNewEntry( EFalse )
	{
	CActiveScheduler::Add( this );
	}

// ---------------------------------------------------------
// CNSmlAlertHandler::~CNSmlAlertHandler()
// Destructor
// ---------------------------------------------------------
CNSmlAlertHandler::~CNSmlAlertHandler()
	{

	Cancel();
	if( !iHistoryArray )
	{
	iHistoryArray->SetOwnerShip( ETrue );
	delete iHistoryArray;
	}
	delete iAlertParser;			
		
	if ( ! iNewEntry )
		{
		delete iAlertInfo;
		delete iHistoryInfo;
		}
	}

// ---------------------------------------------------------
// CNSmlAlertHandler::DoCancel()
// Method from base class
// ---------------------------------------------------------
void CNSmlAlertHandler::DoCancel()
	{
	
	}

// ---------------------------------------------------------
// CNSmlAlertHandler::ProcessAlert()
// Activates alert handler
// ---------------------------------------------------------
void CNSmlAlertHandler::ProcessAlert()
	{
	if ( IsActive() )
		{
		return;
		}
	SetActive();
	TRequestStatus* pStatus = &iStatus;
	User::RequestComplete(pStatus, KErrNone);
	}

// ---------------------------------------------------------
// CNSmlAlertHandler::ConstructL( MNSmlMessageHandler* aMsgHandler )
// Second phase constructor
// ---------------------------------------------------------
void CNSmlAlertHandler::ConstructL( MNSmlMessageHandler* aMsgHandler )
	{
	User::LeaveIfNull(aMsgHandler);
	iMsgHandler = aMsgHandler;
	
    iHistoryInfo = CSyncMLHistoryPushMsg::NewL();

	iAlertInfo = new ( ELeave ) CSmlAlertInfo;

	iHistoryArray = CNSmlHistoryArray::NewL();
	iHistoryArray->SetOwnerShip( EFalse ); // move ownership to history array
	}

// ---------------------------------------------------------
// CNSmlAlertHandler::RunL()
// Method from base class
// ---------------------------------------------------------
void CNSmlAlertHandler::RunL()
    {
    TRAP_IGNORE( DoRunL() );    
    };

// ---------------------------------------------------------
// CNSmlAlertHandler::DoRunL()
// Method from base class
// ---------------------------------------------------------
void CNSmlAlertHandler::DoRunL()
	{

    // Delete member variables
    iHistoryArray->SetOwnerShip( ETrue );
	delete iHistoryArray;
	iHistoryArray = NULL;
	delete iAlertParser;			
	iAlertParser = NULL;

    // These are deleted by iHistoryArray
	iAlertInfo = NULL;
	iHistoryInfo = NULL;

    // Create member variables	
	iHistoryInfo = CSyncMLHistoryPushMsg::NewL();
	iAlertInfo = new ( ELeave ) CSmlAlertInfo;
	iHistoryArray = CNSmlHistoryArray::NewL();
	iHistoryArray->SetOwnerShip( EFalse ); // move ownership to history array
	
	
	// Continue DoRunL method
	TBool message( EFalse );
	TBool quit( ETrue );
	TBool doPop( EFalse );
	
	TPtrC8 pPackage;
	TSmlUsageType type;
	TSmlProtocolVersion version;
	TSmlTransportId bearerType;
	
	//check message
	iMsgHandler->CheckMessage( message, type, version, bearerType );
	iAlertInfo->SetJobControl( CSmlAlertInfo::EDoNotCreateJob );
	iAlertInfo->SetTransportId( bearerType );
	#ifdef __NSML_DEBUG__
		DBG_ARGS(_S("CNSmlAlertHandler::RunL(): TransportId : '%d'"), bearerType );
	#endif // __NSML_DEBUG__

	TInt err( KErrNone );
	
	if ( message )
	    {
	    		
    	iAlertParser = NSmlParserFactory::CreateAlertParserL( type, version, *iAlertInfo, *iHistoryInfo );
    	iAlertParser->CreateBufferL( iMsgHandler->MessageSize() );
    	iMsgHandler->AlertMessage( iAlertParser->Message() );
    					
    	TRAP(err, iAlertParser->ParseMessageL());
    	DBG_FILE_CODE(err, _S8("CNSmlAlertHandler::RunL() : Alert result"));
    	iAlertInfo->SetErrorCode( err );
    	
    	iNewEntry = ETrue;
    	
    	if ( err != KErrNone)
    		{
    		iNewEntry = EFalse;
    		iAlertInfo->SetJobControl( CSmlAlertInfo::EDoNotCreateJob );
    		
    		// Close local connection
    		if ( bearerType != KUidNSmlMediumTypeInternet.iUid )
    			{
    			iMsgHandler->DoDisconnect();	
    			}
    	   		
    		if ( err == KErrCorrupt )
    			{
    			return; 
    			}
    		}
    	else
    		{
    		CheckDigestL( iAlertInfo->Profile(), iHistoryInfo->MsgDigest() );
    					
    		CheckProtocolAndChangeL( version );
    		    					
    		if ( ( type == ESmlDataSync )  && ( version == ESmlVersion1_1_2 ) )
    			{
    			iAlertInfo->SetConfirmation( ETrue );
    			pPackage.Set( iAlertParser->DoMessageCopyLC() );
    			doPop = ETrue;
    			}
    		}

    	delete iAlertParser;
    	iAlertParser = NULL;
	    }
	    
	iAlertInfo->SetConfirmation( FinalizeBeforeJobCreationL() );
	iMsgHandler->CreateJobL( *iAlertInfo, quit, pPackage);                
		
	if ( doPop )
		{
		CleanupStack::PopAndDestroy(); // DoMessageCopyLC()
		}
	
	
	if (! quit)
		{
		ProcessAlert();
		}
	
	}


// ---------------------------------------------------------
// CNSmlAlertHandler::FinalizeBeforeJobCreationL()
// Connects to notifier plug in to show confirmation note and
// waits for the response.
// ---------------------------------------------------------
TBool CNSmlAlertHandler::FinalizeBeforeJobCreationL()
	{
	
	if ( iAlertInfo->Profile() < KMaxDataSyncID )		
		{
		CNSmlDSSettings* settings = CNSmlDSSettings::NewLC();
		
		CNSmlDSProfile* profile = settings->ProfileL( iAlertInfo->Profile() );
		
		if ( !profile )
			{
			CleanupStack::PopAndDestroy(); //settings
			return EFalse;
			}
			
		CleanupStack::PushL( profile );
		
		iAlertInfo->SetTransportId( profile->IntValue( EDSProfileTransportId) );
		iAlertInfo->SetConnectionId( profile->IntValue( EDSProfileTransportId) );
		
		CleanupStack::PopAndDestroy(2); //profile, settings
		}
	else
		{
		CNSmlDMSettings* settings = CNSmlDMSettings::NewLC();
		
		CNSmlDMProfile* profile = settings->ProfileL( iAlertInfo->Profile() );
		
		if ( !profile )
			{
			CleanupStack::PopAndDestroy(); //settings
			return EFalse;
			}
		
		CleanupStack::PushL( profile );
        profile->SetIntValue( EDMProfileSessionId, iAlertInfo->SessionId() );
        profile->SaveL();

		iAlertInfo->SetTransportId( profile->IntValue( EDMProfileTransportId) );
		iAlertInfo->SetConnectionId( profile->IntValue( EDMProfileTransportId) );
		
		CleanupStack::PopAndDestroy(2); //profile, settings
		}
	
	SaveAlertInfoL();
	
	if ( ! iAlertInfo->CreateSession() )
		{
		return ETrue;
		}
	
	return ETrue;
	}

// ---------------------------------------------------------
// CheckDigestL( const TInt aProfileId, const TDesC8& aDigest )
// Checks whether alert has already been handled. Receive count
// is updated if handled before.
// ---------------------------------------------------------
void CNSmlAlertHandler::CheckDigestL( const TInt aProfileId, const TDesC8& aDigest )
	{
	
	if ( aProfileId < KMaxDataSyncID )		
		{
		CNSmlDSSettings* settings = CNSmlDSSettings::NewLC();
		
		CNSmlDSProfile* profile = settings->ProfileL(aProfileId);
		CleanupStack::PushL(profile);
		
		if ( ! profile->HasLogL() )
			{
			CleanupStack::PopAndDestroy(2); //profile, settings
			return;
			}
		
		RReadStream& readStream = profile->LogReadStreamL();
		CleanupClosePushL( readStream );
		
		iHistoryArray->InternalizeL(readStream);
		
		CleanupStack::PopAndDestroy(); //readStream
		
		for (TInt i = 0; i < iHistoryArray->Count(); i++)
			{
			CSyncMLHistoryEntry& hEntry = iHistoryArray->Entry(i);
			CSyncMLHistoryPushMsg * pushMsg = CSyncMLHistoryPushMsg::DynamicCast(&hEntry);
			
			if (pushMsg == NULL)
				continue;
			
			if ( aDigest.Compare( pushMsg->MsgDigest() ) == 0)
				{
				pushMsg->IncReceivedCount();
				iNewEntry = EFalse;
				iAlertInfo->SetConfirmation( iNewEntry );
				}
			}
	
		CleanupStack::PopAndDestroy(2); //profile, settings
		}
	else
		{
		CNSmlDMSettings* settings = CNSmlDMSettings::NewLC();
		
		CNSmlDMProfile* profile = settings->ProfileL(aProfileId);
		CleanupStack::PushL(profile);
		
		if ( ! profile->HasLogL() )
			{
			CleanupStack::PopAndDestroy(2); //profile, settings
			return;
			}
			
		RReadStream& readStream = profile->LogReadStreamL();
		CleanupClosePushL( readStream );

		iHistoryArray->InternalizeL( readStream );
		
		CleanupStack::PopAndDestroy(); //readStream
		
		for (TInt i = 0; i < iHistoryArray->Count(); i++)
			{
			CSyncMLHistoryEntry& hEntry = iHistoryArray->Entry(i);
			CSyncMLHistoryPushMsg * pushMsg = CSyncMLHistoryPushMsg::DynamicCast(&hEntry);
			
			if (pushMsg == NULL)
				continue;
			
			if ( aDigest.Compare( pushMsg->MsgDigest() ) == 0)
				{
				pushMsg->IncReceivedCount();
				iNewEntry = EFalse;
				iAlertInfo->SetConfirmation( iNewEntry );
				}
			}
		CleanupStack::PopAndDestroy(2); //profile, settings
		}
	}

// ---------------------------------------------------------
// CNSmlAlertHandler::SaveAlertInfoL( )
// Saves the result to profile history log and adds new entry
// ---------------------------------------------------------
void CNSmlAlertHandler::SaveAlertInfoL( )
	{
	
	TInt profileId( iAlertInfo->Profile() );
	
	if ( profileId == KNSmlNullId )
		{
		return;
		}
		
	if ( profileId < KMaxDataSyncID )
		{
		CNSmlDSSettings* settings = CNSmlDSSettings::NewLC();
		
		CNSmlDSProfile* profile = settings->ProfileL( profileId );
		CleanupStack::PushL( profile );
		RWriteStream& stream = profile->LogWriteStreamL();
		CleanupClosePushL( stream );
		
		if ( iNewEntry )
			{
			RPointerArray<CSmlAlertInfo> alertInfoArray;
			CleanupClosePushL( alertInfoArray );
			
			alertInfoArray.AppendL( iAlertInfo );
			
			iHistoryInfo->AddAlertsL( alertInfoArray );
			iHistoryArray->AppendEntryL( iHistoryInfo );
			CleanupStack::PopAndDestroy(); //alertInfoArray	
			}
			
		iHistoryArray->ExternalizeL(stream);
		
		CleanupStack::PopAndDestroy(); //stream
		
		profile->WriteStreamCommitL();
	
		CleanupStack::PopAndDestroy(2); // profile, settings
		}
	else
		{
		CNSmlDMSettings* settings = CNSmlDMSettings::NewLC();
		
		CNSmlDMProfile* profile = settings->ProfileL( profileId );
		CleanupStack::PushL( profile );
		
		RWriteStream& stream = profile->LogWriteStreamL();
		CleanupClosePushL( stream );
		
		if ( iNewEntry )
			{
			RPointerArray<CSmlAlertInfo> alertInfoArray;
			CleanupClosePushL( alertInfoArray );
			
			alertInfoArray.AppendL( iAlertInfo );
		
			iHistoryInfo->AddAlertsL( alertInfoArray );
			iHistoryArray->AppendEntryL( iHistoryInfo );
			
			CleanupStack::PopAndDestroy(); //alertInfoArray	
			}
			
		iHistoryArray->ExternalizeL( stream );
		CleanupStack::PopAndDestroy(); //stream
		
		profile->WriteStreamCommitL();
		
		CleanupStack::PopAndDestroy(2); // profile, settings
		}
	}


// ---------------------------------------------------------
// CheckProtocolAndChange(TSmlProtocolVersion& aVersion)
// Checks if profile has different version and chages to alert's
// version if different.
// ---------------------------------------------------------
void CNSmlAlertHandler::CheckProtocolAndChangeL( TSmlProtocolVersion& aVersion ) const
	{
	TInt profileId( iAlertInfo->Profile() );
	
	if ( profileId == KNSmlNullId )
		{
		return;
		}
		
	if ( profileId < KMaxDataSyncID )
		{
		CNSmlDSSettings* settings = CNSmlDSSettings::NewLC();
		CNSmlDSProfile* profile = settings->ProfileL( profileId );
		CleanupStack::PushL( profile );
		
		if ( profile->IntValue( EDSProfileProtocolVersion ) != aVersion )
			{
			profile->SetIntValue( EDSProfileProtocolVersion, (TInt) aVersion );
			profile->SaveL();
			}
			
		CleanupStack::PopAndDestroy(2); // profile, settings
		}
	}

//End of File
