/*
* Copyright (c) 2006 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:  Container store sorting table implementation.
*
*/



#include "ContainerStoreSortingTable.h"
#include "msgstoretypes.h"
#include "MessageStoreClientServer.h"
#include "ContainerStoreDefs.h"
#include "MsgStoreSortResultRowSet.h"
#include "MsgStoreStringSortResultRowSet.h"
#include "ContainerStoreUtils.h"
#include "ContainerStoreEncryption.h"
#include "MsgStoreInMemorySortRowSet.h"
	
_LIT( KSelect,  "SELECT "   );
_LIT( KDelete,  "DELETE "   );
_LIT( KFrom,    " FROM "    );
_LIT( KWhere,   " WHERE "   );
_LIT( KEquals,  " = "       );
_LIT( KOrderBy, " ORDER BY ");
_LIT( KComma,   ", "        );
_LIT( KAsc,     " ASC"      );
_LIT( KDesc,    " DESC"     );
_LIT( KStar,    "* "        );

const TUint KQuerryBufSize = 200;
const TUint KQuerrySizeSmall = 60;

enum TMsgStorePriorityScore
	{
	EPriorityScoreLow    = -10,
	EPriorityScoreNormal =   0,
	EPriorityScoreHigh   =  10
	};

    enum TMsgStoreReadScore
        {
        EReadScoreUnread     = 0,
        EReadScoreRead       = 1,
        EReadScoreReadLocal  = 2,
        EReadScoreReadBoth   = 3
        };

    enum TMsgStoreFlagStatusScore
    	{
    	EFlagStatusScoreNone             = 0,
    	EFlagStatusScoreFollowUpComplete = 10,
    	EFlagStatusScoreFollowUp         = 20 
    	};

    
// ==================================
// CLASS: CContainerStoreSortingTable
// ==================================
// ==========================================================================
// FUNCTION: OpenL
// ==========================================================================
CContainerStoreSortingTable* CContainerStoreSortingTable::OpenL( CContainerStoreUtils&        aUtils,
                                                                 CContainerStoreEncryption&   aEncryption,
                                                                 MSortingTableObserver&       aObserver )
	{
	CContainerStoreSortingTable* self = new(ELeave) CContainerStoreSortingTable( aUtils, aEncryption, aObserver );
	CleanupStack::PushL( self );
	self->OpenTableL();
	CleanupStack::Pop( self );
	return self;
	}
		
// ==========================================================================
// FUNCTION: CreateL
// ==========================================================================
CContainerStoreSortingTable* CContainerStoreSortingTable::CreateL( CContainerStoreUtils&        aUtils,
                                                                   CContainerStoreEncryption&   aEncryption,
                                                                   MSortingTableObserver&       aObserver )
	{
	CContainerStoreSortingTable* self = new(ELeave) CContainerStoreSortingTable( aUtils, aEncryption, aObserver );
	CleanupStack::PushL( self );
	self->CreateTableL();
	CleanupStack::Pop( self );
	return self;
	}

// ==========================================================================
// FUNCTION: Constructor
// ==========================================================================
CContainerStoreSortingTable::CContainerStoreSortingTable( CContainerStoreUtils&        aUtils,
                                                          CContainerStoreEncryption&   aEncryption,
                                                          MSortingTableObserver&       aObserver )
: CContainerStoreTable( aUtils ), iEncryption( aEncryption ), iObserver( aObserver )
	{
	__LOG_CONSTRUCT( "msg", "CContainerStoreSortingTable" )
	__LOG_CLOSE_BETWEEN_WRITES
	} // end constructor
		
// ==========================================================================
// FUNCTION: Destructor
// ==========================================================================
CContainerStoreSortingTable::~CContainerStoreSortingTable()
	{
	iUtils.CloseTable( iTable );
    iEncryptedBuffer.Close();
	__LOG_DESTRUCT	
	} // end destructor

// ==========================================================================
// FUNCTION: OpenTableL
// ==========================================================================
void CContainerStoreSortingTable::OpenTableL()
	{
	__LOG_ENTER( "OpenTableL" )

	iUtils.OpenTableL( iTable, KSortingTableName );
	
	// Get the column numbers for the containers table.
	CDbColSet* colSet = iTable.ColSetL();
	CleanupStack::PushL( colSet );

    iMessageIdColNum      = colSet->ColNo( KSortingTableMessageIdCol );
    iFolderIdColNum       = colSet->ColNo( KSortingTableFolderIdCol );
    iMailBoxIdColNum      = colSet->ColNo( KSortingTableMailBoxIdCol );
    iReceivedDateColNum   = colSet->ColNo( KSortingTableReceivedDateCol );
    iReadUnreadColNum     = colSet->ColNo( KSortingTableReadUnreadCol );
    iPriotiryColNum       = colSet->ColNo( KSortingTablePriorityCol );
    iFlagStatusColNum     = colSet->ColNo( KSortingTableFlagStatusCol );
    iSizeColNum           = colSet->ColNo( KSortingTableSizeCol );
    iAttachmentFlagColNum = colSet->ColNo( KSortingTableAttachmentFlagCol );
    iFromColNum           = colSet->ColNo( KSortingTableFromCol );
    iToColNum             = colSet->ColNo( KSortingTableToCol );
    iSubjectColNum        = colSet->ColNo( KSortingTableSubjectCol );
    iIsEncryptedColNum    = colSet->ColNo( KSortingTableIsEncryptedCol );
	
	CleanupStack::PopAndDestroy( colSet );
	
	// Set the table's index to the ID index.
	User::LeaveIfError( iTable.SetIndex( KSortingTableMessageIdIndex ) );

	__LOG_EXIT	
	}
		
// ==========================================================================
// FUNCTION: CreateTableL
// ==========================================================================
void CContainerStoreSortingTable::CreateTableL()
	{
	__LOG_ENTER( "CreateTableL" )
	
	// Create table columns
	CDbColSet* colSet = CDbColSet::NewLC();
	
	TDbCol msgIdCol( KSortingTableMessageIdCol, EDbColUint32 );
	colSet->AddL( msgIdCol );	
	
	TDbCol folderIdCol( KSortingTableFolderIdCol, EDbColUint32 );
	colSet->AddL( folderIdCol );	
	
	TDbCol mailboxIdCol( KSortingTableMailBoxIdCol, EDbColUint32 );
	colSet->AddL( mailboxIdCol );	
	
	TDbCol receivedDateCol( KSortingTableReceivedDateCol, EDbColInt64 );
	colSet->AddL( receivedDateCol );	
		
	TDbCol readUnreadCol( KSortingTableReadUnreadCol, EDbColInt8 );
	colSet->AddL( readUnreadCol );	
	
	TDbCol priorityCol( KSortingTablePriorityCol, EDbColInt8 );
	colSet->AddL( priorityCol );	
	
	TDbCol flagStatusCol( KSortingTableFlagStatusCol, EDbColInt8 );
	colSet->AddL( flagStatusCol );	
	
	TDbCol sizeCol( KSortingTableSizeCol, EDbColUint32 );
	colSet->AddL( sizeCol );	
	
	TDbCol attCol( KSortingTableAttachmentFlagCol, EDbColInt8 );
	colSet->AddL( attCol );	
    
    TDbCol fromCol( KSortingTableFromCol, EDbColLongBinary );
    colSet->AddL( fromCol );
    
    TDbCol toCol( KSortingTableToCol, EDbColLongBinary );
    colSet->AddL( toCol );  
	
    TDbCol subjectCol( KSortingTableSubjectCol, EDbColLongBinary );
    colSet->AddL( subjectCol );  
    
    TDbCol encryptedCol( KSortingTableIsEncryptedCol, EDbColUint8 );
    colSet->AddL( encryptedCol );  
    
	// Create table.
	iUtils.CreateTableL( KSortingTableName, *colSet );
	
	CleanupStack::PopAndDestroy( colSet );
	
	// Create index for every column except for mailBoxId
	CreateIndexL( KSortingTableName, KSortingTableMessageIdIndex,      KSortingTableMessageIdCol    );
	CreateIndexL( KSortingTableName, KSortingTableFolderIdIndex,       KSortingTableFolderIdCol     );
    CreateIndexL( KSortingTableName, KSortingTableMailBoxIdIndex,      KSortingTableMailBoxIdCol    );
	CreateIndexL( KSortingTableName, KSortingTableReceivedDateIndex,   KSortingTableReceivedDateCol );
	CreateIndexL( KSortingTableName, KSortingTableReadUnreadIndex,     KSortingTableReadUnreadCol,     KSortingTableReceivedDateCol );
	CreateIndexL( KSortingTableName, KSortingTablePriorityIndex,       KSortingTablePriorityCol,       KSortingTableReceivedDateCol );
	CreateIndexL( KSortingTableName, KSortingTableFlagStatusIndex,     KSortingTableFlagStatusCol,     KSortingTableReceivedDateCol );
	CreateIndexL( KSortingTableName, KSortingTableSizeIndex,           KSortingTableSizeCol,           KSortingTableReceivedDateCol );
	CreateIndexL( KSortingTableName, KSortingTableAttachmentFlagIndex, KSortingTableAttachmentFlagCol, KSortingTableReceivedDateCol );
	
	OpenTableL();
			
	__LOG_EXIT
	}

// ==========================================================================
// FUNCTION: AddMessageL
// ==========================================================================
void CContainerStoreSortingTable::AddMessageL( TContainerId             aMessageId, 
											   TContainerId             aFolderId, 
											   TContainerId             aMailboxId, 
                                               RMsgStoreSortableFields& aSortableFields )
	{
	__LOG_ENTER_SUPPRESS( "AddMessageL" )
	
	__LOG_WRITE8_FORMAT3_DEBUG3( "msgId=%x, folderId=%x, mailBoxId=%x", aMessageId, aFolderId, aMailboxId )
	
	iTable.LastL();
	
	InsertRowLC();
	 
	iTable.SetColL( iMessageIdColNum,    aMessageId );
	iTable.SetColL( iFolderIdColNum,     aFolderId );
	iTable.SetColL( iMailBoxIdColNum,    aMailboxId );
    iTable.SetColL( iIsEncryptedColNum, static_cast<TUint8>( iEncryption.IsEncryptionOn() ) );
	
	//write the rest of the fields
	WriteFieldsL( aSortableFields );
	
	PutRowUpdatesL();
    
    //Notify the observer
    iObserver.MessageUpdate( aMessageId, 
    						 aFolderId, 
    						 EMsgStoreAdd, 
    						 iFieldsChanged, 
                             aSortableFields.iFrom, 
                             aSortableFields.iTo, 
                             aSortableFields.iSubject, 
                             aSortableFields.iReceivedDate );
	}
		
// ==========================================================================
// FUNCTION: DeleteMessageL
// ==========================================================================
void CContainerStoreSortingTable::DeleteMessageL( TContainerId aMessageId )
	{
	__LOG_ENTER_SUPPRESS( "DeleteMessageL" )
	
	__LOG_WRITE8_FORMAT1_DEBUG3( "msgId=%x", aMessageId )
	
    SeekL( aMessageId );
    iTable.GetL();
    TContainerId folderId = iTable.ColUint32( iFolderIdColNum );
    
    iTable.DeleteL();
    
    //Notify the observer
    iObserver.MessageUpdate( aMessageId, folderId, EMsgStoreDelete );
	}

// ==========================================================================
// FUNCTION: DeleteMessagesByFolderIdL
// ==========================================================================
void CContainerStoreSortingTable::DeleteMessagesByFolderIdL( TContainerId aFolderId )
	{
	__LOG_ENTER_SUPPRESS( "DeleteMessagesByFolderIdL" )
	__LOG_WRITE8_FORMAT1_DEBUG3( "aFolderId=%x", aFolderId )
	
	TBuf<KQuerryBufSize> queryString;
	queryString.Copy( KDelete );
	queryString.Append( KFrom );
	queryString.Append( KSortingTableName );
	queryString.Append( KWhere );
	queryString.Append( KSortingTableFolderIdCol );
	queryString.Append( KEquals );
	queryString.AppendNum( aFolderId );
	
	iUtils.Execute( queryString );
    
    //Notify the observer
    iObserver.FolderDeleted( aFolderId );
	}

// ==========================================================================
// FUNCTION: DeleteMessagesByMailBoxIdL
// ==========================================================================
void CContainerStoreSortingTable::DeleteMessagesByMailBoxIdL( TContainerId aMailBoxId )
    {
    __LOG_ENTER_SUPPRESS( "DeleteMessagesByMailBoxIdL" )
    __LOG_WRITE8_FORMAT1_DEBUG3( "aMailBoxId=%x", aMailBoxId )
    
    TBuf<KQuerryBufSize> queryString;
    queryString.Copy( KDelete );
    queryString.Append( KFrom );
    queryString.Append( KSortingTableName );
    queryString.Append( KWhere );
    queryString.Append( KSortingTableMailBoxIdCol );
    queryString.Append( KEquals );
    queryString.AppendNum( aMailBoxId );
    
    iUtils.Execute( queryString );
    
    //Notify the observer
    iObserver.MailBoxDeleted( aMailBoxId );
    }

// ==========================================================================
// FUNCTION: UpdateMessageL
// ==========================================================================
void CContainerStoreSortingTable::UpdateMessageL( TContainerId aMessageId, 
                                                  RMsgStoreSortableFields& aSortableFields )
	{
	__LOG_ENTER_SUPPRESS( "UpdateMessageL" )
	
	__LOG_WRITE8_FORMAT4_DEBUG3( "msgId=%x, flags=%x, ssize=%d csize=%d", aMessageId, 
                                                                aSortableFields.iFlags, 
                                                                aSortableFields.iSizeOnServer,
                                                                aSortableFields.iSizeOnClient )
	
	SeekL( aMessageId );
    
    iTable.GetL();
    TContainerId folderId = iTable.ColUint32( iFolderIdColNum );
	
	PrepareRowForUpdateLC();
	
	WriteFieldsL( aSortableFields, EFalse );
	
	PutRowUpdatesL();
    
    if ( iFieldsChanged > 0 )
        {
        iObserver.MessageUpdate( aMessageId, 
        						 folderId, 
        						 EMsgStoreUpdateProperties, 
        						 iFieldsChanged,
                                 aSortableFields.iFrom, 
                                 aSortableFields.iTo, 
                                 aSortableFields.iSubject, 
                                 aSortableFields.iReceivedDate );
        }
	}

// ==========================================================================
// FUNCTION: UpdateMessageFolderL
// ==========================================================================
void CContainerStoreSortingTable::UpdateMessageFolderL( TContainerId aMessageId, TContainerId aNewFolderId )
	{
	__LOG_ENTER_SUPPRESS( "UpdateMessageFolderL" )
	
	__LOG_WRITE8_FORMAT2_DEBUG3( "msgId=%x, aNewFolder=%x", aMessageId, aNewFolderId )
    
	SeekL( aMessageId );
    iTable.GetL();
    
    TContainerId oldFolderId = iTable.ColUint32( iFolderIdColNum );
    
    if ( oldFolderId != aNewFolderId )
        {
    	
    	PrepareRowForUpdateLC();
    	
    	iTable.SetColL( iFolderIdColNum, aNewFolderId );
    	
    	PutRowUpdatesL();
        
        //send a notification for delete from old folder 
        iObserver.MessageUpdate( aMessageId, oldFolderId, EMsgStoreDelete );
        
        //send a notification for new message to the new folder
        RBuf from;
        CleanupClosePushL( from );
        ReadStringFieldL( iTable, iFromColNum, from );
        
        RBuf to;
        CleanupClosePushL( to );
        ReadStringFieldL( iTable, iToColNum, to );
        
        RBuf subject;
        CleanupClosePushL( subject );
        ReadStringFieldL( iTable, iSubjectColNum, subject );
        
        TInt64 date = iTable.ColInt64( iReceivedDateColNum );
        
        iObserver.MessageUpdate( aMessageId, aNewFolderId, EMsgStoreAdd, 0, from, to, subject, date );
        
        CleanupStack::PopAndDestroy( &subject );
        CleanupStack::PopAndDestroy( &to );
        CleanupStack::PopAndDestroy( &from );
        
        }    
	}
		
// ==========================================================================
// FUNCTION: SortL
// ==========================================================================
CMsgStoreSortResultRowSet* CContainerStoreSortingTable::SortL( const TMsgStoreSortCriteria& aSortCriteria,  TBool aInMemorySort  )
	{
	__LOG_ENTER_SUPPRESS( "SortL" )
    
	CMsgStoreSortResultRowSet* result = NULL;
    
    if ( aSortCriteria.iSortBy == EMsgStoreSortBySender ||
         aSortCriteria.iSortBy == EMsgStoreSortByRecipient     ||   
         aSortCriteria.iSortBy == EMsgStoreSortBySubject )
        {
        result = SortByStringFieldL( aSortCriteria );
        }
    else if ( aInMemorySort )
    	{
    	result = InMemorySortL( aSortCriteria );
    	}
    else
        {

    	TBuf<KQuerryBufSize> queryString;
    	queryString.Copy( KSelect );
        queryString.Append( KStar );
    	queryString.Append( KFrom );
    	queryString.Append( KSortingTableName );
    	queryString.Append( KWhere );
    	queryString.Append( KSortingTableFolderIdCol );
    	queryString.Append( KEquals );
    	queryString.AppendNum( aSortCriteria.iFolderId );
    	queryString.Append( KOrderBy );
    	
    	switch( aSortCriteria.iSortBy )
    		{
    		case EMsgStoreSortByReceivedDate:
    			queryString.Append( KSortingTableReceivedDateCol );
    			break;
    		
    		case EMsgStoreSortByPriority:
    			queryString.Append( KSortingTablePriorityCol );
    			break;
    			
    		case EMsgStoreSortByFlagStatus:
    			queryString.Append( KSortingTableFlagStatusCol );
    			break;
    		
    		case EMsgStoreSortByUnRead:
    			queryString.Append( KSortingTableReadUnreadCol );
    			break;
    		
    		case EMsgStoreSortBySize:
    			queryString.Append( KSortingTableSizeCol );
    			break;
    		
    		case EMsgStoreSortByAttachment:
    			queryString.Append( KSortingTableAttachmentFlagCol );
    			break;
    		
    		default:
    			User::Leave( KErrNotSupported );
    			break;
    		}
    	
    	//add the sort order
    	AppendSortOrder( queryString, aSortCriteria.iSortOrder );
    	
    	//add the secondary sort field if primary is not received date
    	if ( aSortCriteria.iSortBy != EMsgStoreSortByReceivedDate )
    		{
    		queryString.Append( KComma );
    		queryString.Append( KSortingTableReceivedDateCol );
    		AppendSortOrder( queryString, aSortCriteria.iSecondarySortOrder );
    		}
    	
    	__LOG_WRITE_FORMAT1_INFO( "query=%S", &queryString );
    
    	result = CMsgStoreSortResultRowSet::NewL( aSortCriteria, 
    	                                          iMessageIdColNum,
    	                                          iReceivedDateColNum,
    	                                          iReadUnreadColNum,
    	                                          iPriotiryColNum,
    	                                          iFlagStatusColNum,
    	                                          iSizeColNum,
    	                                          iAttachmentFlagColNum );
    	CleanupStack::PushL( result );
    	
        iUtils.PopulateViewL( result->DbView(), queryString );
        
        __LOG_WRITE_FORMAT1_INFO( "rowCounts=%d", result->DbView().CountL() );
        
        CleanupStack::Pop( result );
        }
    
        return result;
	}


// ==========================================================================
// FUNCTION: SortByStringFieldL
// ==========================================================================
CMsgStoreStringSortResultRowSet* CContainerStoreSortingTable::SortByStringFieldL( const TMsgStoreSortCriteria& aSortCriteria  )
    {
    __LOG_ENTER_SUPPRESS( "SortByStringFieldL" )
    
    CMsgStoreStringSortResultRowSet* result = CMsgStoreStringSortResultRowSet::NewL( aSortCriteria, *this );
    CleanupStack::PushL( result );
    TBuf<KQuerryBufSize> queryString;
    queryString.Copy( KSelect );
    queryString.Append( KStar );
    queryString.Append( KFrom );
    queryString.Append( KSortingTableName );
    queryString.Append( KWhere );
    queryString.Append( KSortingTableFolderIdCol );
    queryString.Append( KEquals );
    queryString.AppendNum( aSortCriteria.iFolderId );

    iUtils.PopulateViewL( result->DbView(), queryString );
    
    ReEvaluateStringViewL( *result, aSortCriteria.iSortBy );

    result->SortL();
    
    CleanupStack::Pop( result );
    
    return result;
    }

// ==========================================================================
// FUNCTION: InMemorySortL
// ==========================================================================
CMsgStoreInMemorySortRowSet* CContainerStoreSortingTable::InMemorySortL( const TMsgStoreSortCriteria& aSortCriteria  )
    {
    __LOG_ENTER_SUPPRESS( "InMemorySortL" )
    
    CMsgStoreInMemorySortRowSet* result = CMsgStoreInMemorySortRowSet::NewL( aSortCriteria, *this );
    CleanupStack::PushL( result );
    
    TBuf<KQuerryBufSize> queryString;
    queryString.Copy( KSelect );
    queryString.Append( KStar );
    queryString.Append( KFrom );
    queryString.Append( KSortingTableName );
    queryString.Append( KWhere );
    queryString.Append( KSortingTableFolderIdCol );
    queryString.Append( KEquals );
    queryString.AppendNum( aSortCriteria.iFolderId );

    iUtils.PopulateViewL( result->DbView(), queryString );
    
    ReEvaluateInMemoryViewL( *result, aSortCriteria.iSortBy );

    result->SortL();
    
    CleanupStack::Pop( result );
    
    return result;
    }


// ==========================================================================
// FUNCTION: ReEvaluateStringViewL
// ==========================================================================
void CContainerStoreSortingTable::ReEvaluateStringViewL( CMsgStoreStringSortResultRowSet& aResult, TMsgStoreSortByField aSortBy )
    {
    __LOG_ENTER( "ReEvaluateStringViewL" )
    
    iUtils.SuspendCompactionLC();
    
    TInt stringColNum = GetColumnNumL( aSortBy );
    
    RDbView& view = aResult.DbView();
    
    TBool found = view.FirstL();
    
    CStringRow* reuseRow = NULL;
    
    RBuf stringVal;
    CleanupClosePushL( stringVal );
    
    while( found )
        {
        view.GetL();
        
        //get the message id, received date, and the desired string
        TContainerId msgId = view.ColUint32( iMessageIdColNum );  
        TInt64 receivedDate = view.ColInt64( iReceivedDateColNum );
        
        ReadStringFieldL( view, stringColNum, stringVal );
        
        //To reduce memory fragmentation, we re-use the row object whenever possible
        CStringRow* row;
        if ( reuseRow  )
            {
            row = reuseRow;
            row->ResetL( msgId, receivedDate, stringVal );
            CleanupStack::Pop( reuseRow );
            }
        else
            {
            row = CStringRow::NewL( msgId, receivedDate, stringVal );
            }
        
        reuseRow = aResult.AppendL( row );
        
        if ( reuseRow )
            {
            CleanupStack::PushL( reuseRow );
            }
        
        found = view.NextL();
        
        if ( !found && reuseRow )
            {
            //no more rows, delete the reuseRow
            CleanupStack::PopAndDestroy( reuseRow );
            }
            
        } // end while
    
    CleanupStack::PopAndDestroy( &stringVal );
    
    iUtils.ResumeCompaction();
    
    __LOG_EXIT
    }

// ==========================================================================
// FUNCTION: ReEvaluateInMemoryViewL
// ==========================================================================
void CContainerStoreSortingTable::ReEvaluateInMemoryViewL( CMsgStoreInMemorySortRowSet& aResult, TMsgStoreSortByField aSortBy )
    {
    __LOG_ENTER( "ReEvaluateInMemoryViewL" )
    
    iUtils.SuspendCompactionLC();
    
    RDbView& view = aResult.DbView();
    TBool found = view.FirstL();
    
    while( found )
        {
        view.GetL();
        
        //get the message id, received date, and the desired string
        TContainerId msgId = view.ColUint32( iMessageIdColNum );  
        TInt64 receivedDate = view.ColInt64( iReceivedDateColNum );

        TInt32 value = GetIntValue( view, aSortBy );
        
        CIntegerRow* row = new(ELeave) CIntegerRow( msgId, receivedDate, value );
        CleanupStack::PushL( row );
        
        aResult.AppendL( row );
        
        CleanupStack::Pop( row );
        
        found = view.NextL();            
        } // end while
   
    iUtils.ResumeCompaction();
    
    __LOG_EXIT
    }


// ==========================================================================
// FUNCTION: GetStringRowL
// ==========================================================================
CStringRow* CContainerStoreSortingTable::GetStringRowL( RDbView& aView, TContainerId aMessageId, TMsgStoreSortByField aSortBy )
    {

    TBuf<KQuerrySizeSmall> queryString;
    
    queryString.Copy( KSortingTableMessageIdCol );
    queryString.Append( KEquals );
    queryString.AppendNum( aMessageId );
    
    User::LeaveIfError( aView.FindL( RDbRowSet::EForwards, queryString ) );
    
    TInt stringColNum = GetColumnNumL( aSortBy ) ;
    
    aView.GetL();
    
    //get the message id, received date, and the desired string
    TContainerId msgId = aView.ColUint32( iMessageIdColNum );  
    TInt64 receivedDate = aView.ColInt64( iReceivedDateColNum );
    
    RBuf stringVal;
    CleanupClosePushL( stringVal );
    
    ReadStringFieldL( aView, stringColNum, stringVal );
    
    CStringRow* row = CStringRow::NewL( msgId, receivedDate, stringVal );
    
    CleanupStack::PopAndDestroy( &stringVal );
    
    return row;
    }

// ==========================================================================
// FUNCTION: GetIntValue
// ==========================================================================
TInt32 CContainerStoreSortingTable::GetIntValue( RDbRowSet& aRowSet, TMsgStoreSortByField aSortBy )
	{
	TInt32 value = 0;
	if ( aSortBy !=  EMsgStoreSortByReceivedDate )
		{
		switch( aSortBy )
			{
			case EMsgStoreSortByPriority:
			    value = aRowSet.ColInt8( iPriotiryColNum );
				break;
				
			case EMsgStoreSortByFlagStatus:
			    value = aRowSet.ColInt8( iFlagStatusColNum );
				break;
			
			case EMsgStoreSortByUnRead:
				value = aRowSet.ColInt8( iReadUnreadColNum );
				break;
			
			case EMsgStoreSortBySize:
				value = static_cast<TInt32>(aRowSet.ColUint32 ( iSizeColNum ));
				break;
			
			case EMsgStoreSortByAttachment:
				value = aRowSet.ColInt8( iAttachmentFlagColNum );
				break;
			
			default:
				break;
			}
		}
	return value;
	}

// ==========================================================================
// FUNCTION: GetIntValueL
// ==========================================================================
TInt32 CContainerStoreSortingTable::GetIntValueL( TMsgStoreSortByField aSortBy, TContainerId aMessageId )
	{
    TBuf<KQuerrySizeSmall> queryString;
    
    queryString.Copy( KSortingTableMessageIdCol );
    queryString.Append( KEquals );
    queryString.AppendNum( aMessageId );
    
    User::LeaveIfError( iTable.FindL( RDbRowSet::EForwards, queryString ) );
    
    iTable.GetL();
    
    return GetIntValue( iTable, aSortBy );	
	}

// ==========================================================================
// FUNCTION: SeekL
// ==========================================================================
void CContainerStoreSortingTable::SeekL( TContainerId aMessageId )
    {
    __LOG_ENTER_SUPPRESS("SeekL")
    if ( !iTable.SeekL( aMessageId ) )
        {
        __LOG_WRITE8_FORMAT1_ERROR( "Message id %x not found", aMessageId )
        User::Leave( KErrNotFound );
        }
    }

// ==========================================================================
// FUNCTION: WriteFieldsL
// ==========================================================================
void CContainerStoreSortingTable::WriteFieldsL( RMsgStoreSortableFields& aSortableFields, TBool aIsNew )
	{
    
    iFieldsChanged = 0;
    
    SetFlagIfChangedInt64( aSortableFields.iReceivedDate, iReceivedDateColNum, aIsNew, EMaskReceivedDate );
	iTable.SetColL( iReceivedDateColNum, aSortableFields.iReceivedDate );
	
    /******************************
	* process the read/unread flag
    *******************************/
    TUint flags = aSortableFields.iFlags;
    
	TInt8 readFlag = EReadScoreUnread; 
	if ( (flags & EMsgStoreFlag_Read) && (flags & EMsgStoreFlag_Read_Locally) ) 
	    {
	    readFlag = EReadScoreReadBoth;
	    }
	else if ( flags & EMsgStoreFlag_Read )
	    {
        readFlag = EReadScoreRead;
	    }
	else if ( flags & EMsgStoreFlag_Read_Locally )
	    {
        readFlag = EReadScoreReadLocal;
	    }
	
    SetFlagIfChangedInt8( readFlag, iReadUnreadColNum, aIsNew, EMaskReadUnread );
    
    //set the new value
	iTable.SetColL( iReadUnreadColNum, readFlag );
    
    /******************************
    * process the priority flag
    *******************************/
	TInt8 priority = EPriorityScoreNormal;
	if ( flags & EMsgStoreFlag_Important )
		{
		priority = EPriorityScoreHigh;
		}
	else if ( flags & EMsgStoreFlag_Low )
		{
		priority = EPriorityScoreLow;
		}
    SetFlagIfChangedInt8( priority, iPriotiryColNum, aIsNew, EMaskPriotiry );
	iTable.SetColL( iPriotiryColNum, priority );
	
    /******************************
     * process the follow up flag
     *******************************/
	TInt8 flagStatus = EFlagStatusScoreNone;
	if ( flags & EMsgStoreFlag_FollowUp )
		{
		flagStatus = EFlagStatusScoreFollowUp;
		}
	else if ( flags &  EMsgStoreFlag_FollowUpComplete )
		{
		flagStatus = EFlagStatusScoreFollowUpComplete;
		}
    SetFlagIfChangedInt8( flagStatus, iFlagStatusColNum, aIsNew, EMaskFlagStatus );
	iTable.SetColL( iFlagStatusColNum, flagStatus );
    
    //process the attachment flag
    TInt8 attachmentFalg = static_cast<TInt8>(flags & EMsgStoreFlag_Attachments);
    SetFlagIfChangedInt8( attachmentFalg, iAttachmentFlagColNum, aIsNew, EMaskAttachmentFlag );
	iTable.SetColL( iAttachmentFlagColNum, attachmentFalg );
	
    //if size on server is available, use it, otherwise, use the size on client
    TUint size = aSortableFields.iSizeOnServer > 0 ? aSortableFields.iSizeOnServer : aSortableFields.iSizeOnClient;
    SetFlagIfChangedUint( size, iSizeColNum, aIsNew, EMaskSize );
	iTable.SetColL( iSizeColNum, size );
    
    //write the subject
    WriteStringFieldL( iSubjectColNum, aSortableFields.iSubject, aIsNew, EMaskSubject );
    
    //write the from
    WriteStringFieldL( iFromColNum, aSortableFields.iFrom, aIsNew, EMaskFrom );
    
    //write the to field
    WriteStringFieldL( iToColNum, aSortableFields.iTo, aIsNew, EMaskTo );
	}

// ==========================================================================
// FUNCTION: AppendSortOrder
// ==========================================================================
void CContainerStoreSortingTable::AppendSortOrder( TDes& aQuery, TMsgStoreSortOrder aOrder )
	{
	if ( aOrder == EMsgStoreSortDescending )
		{
		aQuery.Append( KDesc );
		}
	else
		{
		aQuery.Append( KAsc );
		}
	}

// ==========================================================================
// FUNCTION: WriteStringFieldL
// ==========================================================================
void CContainerStoreSortingTable::WriteStringFieldL( TInt                 aColumnNum, 
                                                     const TDesC&         aString, 
                                                     TBool                aIsNew, 
                                                     TSortableFieldsMasks aMask )
    {
    __LOG_ENTER_SUPPRESS( "WriteStringFieldL" )
    __LOG_WRITE_FORMAT2_DEBUG3( "col=%d, string=%S", aColumnNum, &aString )
    
    TBool changed = ETrue;
    
    if ( !aIsNew )
        {
        RBuf curVal;
        CleanupClosePushL( curVal );
        ReadStringFieldL( iTable, aColumnNum, curVal );
        if ( curVal == aString )
            {
            changed = EFalse;
            }
        CleanupStack::PopAndDestroy( &curVal );
        }
        
    if ( changed )
        {
        iFieldsChanged |= aMask;
        }
    
    if ( aString.Length() == 0 )
        {
        iTable.SetColNullL( aColumnNum );
        }
    else if ( changed )
        {
        WriteStringL( aColumnNum, aString, IsEncrypted( iTable ) );
        }  //end if
    }

// ==========================================================================
// FUNCTION: WriteStringL
// ==========================================================================
void CContainerStoreSortingTable::WriteStringL( TInt         aColumnNum, 
                                                const TDesC& aString, 
                                                TBool        aIsRowEncrypted )
    {
    
    const TUint8* valuePtr8 = reinterpret_cast<const TUint8*>( aString.Ptr() );
    TPtrC8 string8;
    string8.Set( valuePtr8, aString.Length() * 2 );
    
    if ( aIsRowEncrypted && string8.Length() > 0 )
        {
        RBuf8 encryptedBuffer;
        encryptedBuffer.CreateL( string8.Length() + iEncryption.BlockSizeL() );
        CleanupClosePushL( encryptedBuffer );
        
        RBuf8 paddedBuffer;
        paddedBuffer.CreateL( string8.Length() + iEncryption.BlockSizeL() );       
        CleanupClosePushL( paddedBuffer );
        
        paddedBuffer.Copy( string8 );
        iEncryption.AddPaddingL( paddedBuffer, iEncryption.BlockSizeL() );
        
        iEncryption.EncryptL( paddedBuffer, encryptedBuffer );
        
        CleanupStack::PopAndDestroy( &paddedBuffer );
        
        iUtils.WriteLongColumnL( iTable, aColumnNum, encryptedBuffer );
        
        CleanupStack::PopAndDestroy( &encryptedBuffer );
        }
    else
        {
        iUtils.WriteLongColumnL( iTable, aColumnNum, string8 );
        }
    
    }


// ==========================================================================
// FUNCTION: ReadStringFieldL
// ==========================================================================
void CContainerStoreSortingTable::ReadStringFieldL( RDbRowSet& aView, TInt aColumnNum, RBuf& aString )
    {
    __LOG_ENTER_SUPPRESS( "ReadStringFieldL" )
    
    aString.SetLength( 0 );
    
    iUtils.ReadLongColumnL( aView, aColumnNum, iEncryptedBuffer );
    
    RBuf8 string8;
    CleanupClosePushL( string8 );
    string8.Create( iEncryptedBuffer.Length() );
    
    if( aString.Size() < iEncryptedBuffer.Length() )
        {        
        aString.ReAllocL( iEncryptedBuffer.Length() );
        } // end if
    
    if ( IsEncrypted( aView ) )
        {
        if( iEncryptedBuffer.Length() > 0 )
            {        
            iEncryption.DecryptL( iEncryptedBuffer, string8 );        
            iEncryption.RemovePaddingL( string8 );
            } // end if
        }
    else
        {
        string8.Copy( iEncryptedBuffer );
        }
    
    const TUint16* valuePtr16 = reinterpret_cast<const TUint16*>( string8.Ptr() );
    TPtrC16 string16;
    string16.Set( valuePtr16, string8.Length() / 2 );
    aString.Copy( string16 );
    
    CleanupStack::PopAndDestroy( &string8 );
    
    __LOG_WRITE_FORMAT2_DEBUG3( "col=%d, string=%S", aColumnNum, &aString )
    }


// ==========================================================================
// FUNCTION: EncryptFirstL
// ==========================================================================
TBool CContainerStoreSortingTable::EncryptFirstL( TDbBookmark& aNextRow )
    {
    __LOG_ENTER( "EncryptFirstL" )
    
    TBool found = iUtils.FindFirstEncryptedOrUnencryptedL( iTable, KSortingTableIsEncryptedCol, EFalse, aNextRow );
    
    __LOG_EXIT
    
    return found;
    }

// ==========================================================================
// FUNCTION: EncryptNextL
// ==========================================================================
TBool CContainerStoreSortingTable::EncryptNextL( TDbBookmark& aNextRow )
    {
    __LOG_ENTER( "EncryptNextL" )
    
    TBool hasMore = EFalse;
    
    TRAPD( err, iTable.GotoL( aNextRow ) );
    if ( err == KErrNotFound )
        {
        hasMore = EncryptFirstL( aNextRow );
        }
    else if ( err != KErrNone )
        {
        User::Leave( err );        
        }
    else
        {
        iTable.GetL();
        if ( !IsEncrypted( iTable ) )
            {
            RBuf string;
            CleanupClosePushL( string );
            
            PrepareRowForUpdateLC();
            
            //process the from field
            ReadStringFieldL( iTable, iFromColNum, string );
            WriteStringL( iFromColNum, string, ETrue );  //encrypt before write
            
            //process the to field
            ReadStringFieldL( iTable, iToColNum, string );
            WriteStringL( iToColNum, string, ETrue );    //encrypt before write
            
            //process the subject field
            ReadStringFieldL( iTable, iSubjectColNum, string );
            WriteStringL( iSubjectColNum, string, ETrue );  //encrypt before write
            
            //update the isEncrypted column
            SetEncryptedL( ETrue );
            
            PutRowUpdatesL();
            
            CleanupStack::PopAndDestroy( &string );
            }
        
        hasMore = iTable.NextL();
        if ( hasMore )
            {
            __LOG_WRITE_INFO("hasMore is TRUE")
            aNextRow = iTable.Bookmark();
            }
        }

    __LOG_EXIT
    
    return hasMore;
    }

// ==========================================================================
// FUNCTION: DecryptFirstL
// ==========================================================================
TBool CContainerStoreSortingTable::DecryptFirstL( TDbBookmark& aNextRow )
    {
    __LOG_ENTER( "DecryptFirstL" )
    
    TBool found = iUtils.FindFirstEncryptedOrUnencryptedL( iTable, KSortingTableIsEncryptedCol, ETrue, aNextRow );
    
    __LOG_EXIT
    
    return found;
    }

// ==========================================================================
// FUNCTION: DecryptNextL
// ==========================================================================
TBool CContainerStoreSortingTable::DecryptNextL( TDbBookmark& aNextRow )
    {
    __LOG_ENTER( "DecryptNextL" )
    
    TBool hasMore = EFalse;
    
    TRAPD( err, iTable.GotoL( aNextRow ) );
    if ( err == KErrNotFound )
        {
        hasMore = DecryptFirstL( aNextRow );
        }
    else if ( err != KErrNone )
        {
        User::Leave( err );        
        }
    else
        {
        iTable.GetL();
        if ( IsEncrypted( iTable ) )
            {
            RBuf string;
            CleanupClosePushL( string );
            
            PrepareRowForUpdateLC();
            
            //process the from field
            ReadStringFieldL( iTable, iFromColNum, string );
            WriteStringL( iFromColNum, string, EFalse );  //do not encrypt
            
            //process the to field
            ReadStringFieldL( iTable, iToColNum, string );
            WriteStringL( iToColNum, string, EFalse );  //do not encrypt
            
            //process the subject field
            ReadStringFieldL( iTable, iSubjectColNum, string );
            WriteStringL( iSubjectColNum, string, EFalse );  //do not encrypt
            
            //update the isEncrypted column
            SetEncryptedL( EFalse );
            
            PutRowUpdatesL();
            
            CleanupStack::PopAndDestroy( &string );
            }
        
        hasMore = iTable.NextL();
        if ( hasMore )
            {
            __LOG_WRITE_INFO("hasMore is TRUE")
            aNextRow = iTable.Bookmark();
            }
        }

    __LOG_EXIT
    
    return hasMore;
    }

// ==========================================================================
// FUNCTION: SetFlagIfChangedInt8
// ==========================================================================
void CContainerStoreSortingTable::SetFlagIfChangedInt8( TInt8 newVal, TInt aColNum, TBool aIsNew, TSortableFieldsMasks aMask )
    {
    TInt8 curVal = newVal;
    if ( !aIsNew )
        {
        curVal = iTable.ColInt8( aColNum );
        }
    
    if ( aIsNew || curVal != newVal )
        {
        iFieldsChanged |= aMask;
        }
    }

// ==========================================================================
// FUNCTION: SetFlagIfChangedUint
// ==========================================================================
void CContainerStoreSortingTable::SetFlagIfChangedUint( TUint newVal, TInt aColNum, TBool aIsNew, TSortableFieldsMasks aMask )
    {
    TInt8 curVal = newVal;
    if ( !aIsNew )
        {
        curVal = iTable.ColUint32( aColNum );
        }
    
    if ( aIsNew || curVal != newVal )
        {
        iFieldsChanged |= aMask;
        }
    }

// ==========================================================================
// FUNCTION: SetFlagIfChangedInt64
// ==========================================================================
void CContainerStoreSortingTable::SetFlagIfChangedInt64( TInt64 newVal, TInt aColNum, TBool aIsNew, TSortableFieldsMasks aMask )
    {
    TInt64 curVal = newVal;
    if ( !aIsNew )
        {
        curVal = iTable.ColInt64( aColNum );
        }
    
    if ( aIsNew || curVal != newVal )
        {
        iFieldsChanged |= aMask;
        }
    }

// ==========================================================================
// FUNCTION: GetColumnNumL
// ==========================================================================
TInt CContainerStoreSortingTable::GetColumnNumL( TMsgStoreSortByField aSortBy )
    {
    TInt stringColNum = 0;
    if ( aSortBy == EMsgStoreSortBySender )
        {
        stringColNum = iFromColNum;
        }
    else if ( aSortBy == EMsgStoreSortByRecipient )
        {
        stringColNum = iToColNum;
        }
    else if ( aSortBy == EMsgStoreSortBySubject )
        {
        stringColNum = iSubjectColNum;
        }
    else
        {
        User::Leave(KErrArgument);
        }
    return stringColNum;
    }

// ==========================================================================
// FUNCTION: IsEncrypted
// ==========================================================================
TBool CContainerStoreSortingTable::IsEncrypted( RDbRowSet& aRowSet )
    {
    return static_cast<TBool>( aRowSet.ColUint8( iIsEncryptedColNum ) );
    }

// ==========================================================================
// FUNCTION: SetEncryptedL
// ==========================================================================
void CContainerStoreSortingTable::SetEncryptedL( TBool aIsEncrypted )
    {
    iTable.SetColL( iIsEncryptedColNum, static_cast<TUint8>(aIsEncrypted) );
    }

