/*
* Copyright (c) 2010 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: 
*
*/

#include "c12keypredictivesearchtable.h"
#include "dbsqlconstants.h"
#include "cntitem.h"
#include "c12keykeymap.h"
#include <QStringList>

// This macro suppresses log writes
//#define NO_PRED_SEARCH_LOGS
#include "predictivesearchlog.h"


// Max amount of tokens stored from contact
const TInt KMaxTokens = 4;

// How many digits are stored at most in the numeric field
// Since BIGINT is a signed 64-bit integer, store only 15 hexadecimal characters
// to prevent overflow when comparing upper and lower limits.
const TInt KMaxTokenLength = 15;

const quint64 KConversionError = 0xeeeeeeeeeeeeeee;

#define MAPPED_CHAR_FOR_STAR	'a'
#define MAPPED_CHAR_FOR_HASH	'b'

const QChar KPadChar = 'f'; // Pad with hex-digit 0xF


/**
@param aDatabase A handle to the database.

@return A pointer to a new C12keyPredictiveSearchTable object.
*/
C12keyPredictiveSearchTable*
C12keyPredictiveSearchTable::NewL(RSqlDatabase& aDatabase)
	{
	PRINT(_L("C12keyPredictiveSearchTable::NewL"));
	C12keyPredictiveSearchTable* self = C12keyPredictiveSearchTable::NewLC(aDatabase);
	CleanupStack::Pop(self);
	PRINT(_L("C12keyPredictiveSearchTable::NewL ends"));
	return self;
	}


/**
@param aDatabase A handle to the database.

@return A pointer to a new C12keyPredictiveSearchTable object.
*/
C12keyPredictiveSearchTable*
C12keyPredictiveSearchTable::NewLC(RSqlDatabase& aDatabase)
	{
	PRINT(_L("C12keyPredictiveSearchTable::NewLC"));
	C12keyPredictiveSearchTable* self =
		new (ELeave) C12keyPredictiveSearchTable(aDatabase);
	CleanupStack::PushL(self);
	self->ConstructL();
	PRINT(_L("C12keyPredictiveSearchTable::NewLC ends"));
	return self;
	}


/**
Destructor
*/
C12keyPredictiveSearchTable::~C12keyPredictiveSearchTable()
	{
	PRINT(_L("C12keyPredictiveSearchTable dtor"));
	PRINT(_L("C12keyPredictiveSearchTable dtor ends"));
	}


/**
Creates the 12-key predictive search tables and its indexes in the database.
*/
void C12keyPredictiveSearchTable::CreateTableL()
	{
	PRINT(_L("C12keyPredictiveSearchTable::CreateTableL"));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable0Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable1Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable2Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable3Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable4Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable5Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable6Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable7Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable8Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable9Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable10Stmnt));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateTable11Stmnt));


	PRINT(_L("Create indexes"));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable0));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable0));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable0));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable0));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable1));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable1));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable1));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable1));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable2));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable2));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable2));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable2));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable3));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable3));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable3));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable3));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable4));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable4));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable4));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable4));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable5));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable5));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable5));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable5));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable6));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable6));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable6));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable6));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable7));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable7));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable7));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable7));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable8));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable8));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable8));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable8));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable9));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable9));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable9));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable9));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable10));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable10));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable10));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable10));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbrIndexTable11));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr2IndexTable11));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr3IndexTable11));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateNbr4IndexTable11));

	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable0));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable0));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable1));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable1));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable2));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable2));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable3));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable3));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable4));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable4));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable5));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable5));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable6));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable6));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable7));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable7));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable8));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable8));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable9));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable9));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable10));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable10));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateFNIndexInTable11));
	User::LeaveIfError(iDatabase.Exec(KPredSearchCreateLNIndexInTable11));

	PRINT(_L("C12keyPredictiveSearchTable::CreateTableL ends"));
	}


QList<QChar> C12keyPredictiveSearchTable::FillAllTables() const
	{
	QList<QChar> tables;

	const TInt KLargestDigitKey = '9';
	for (TInt i = '0'; i <= KLargestDigitKey; ++i)
		{
		QChar ch = i;
		tables.append(ch);
		}
	tables.append(MAPPED_CHAR_FOR_STAR);
	tables.append(MAPPED_CHAR_FOR_HASH);

	return tables;
	}


HBufC* C12keyPredictiveSearchTable::TableNameL(const QChar aCh) const
	{
	// Enough space for the longest table name
    HBufC* tableName = HBufC::NewLC(KSqlContactPredSearchTable11().Length());
    TPtr ptr = tableName->Des();
	ptr.Append(GetTableNameL(aCh));
	CleanupStack::Pop(tableName);
	return tableName;
	}


TBool C12keyPredictiveSearchTable::IsValidChar(const QChar aChar) const
	{
	return (aChar >= '0' && aChar <= '9') ||
			aChar == MAPPED_CHAR_FOR_STAR ||
			aChar == MAPPED_CHAR_FOR_HASH;
	}


void C12keyPredictiveSearchTable::FillKeyboardSpecificFieldsL(
	RSqlStatement& aSqlStatement,
	QStringList aTokens)
	{
	const TDesC* paramNames[] = {
		&KPredSearchNameAsNumberParam,
		&KPredSearchNameAsNumber2Param,
		&KPredSearchNameAsNumber3Param,
		&KPredSearchNameAsNumber4Param};
	for (TInt i = 0; i < aTokens.count(); ++i)
		{
		quint64 hex(0);
		QT_TRYCATCH_LEAVING(hex = ConvertToHex(aTokens[i]));
		if (hex == KConversionError)
			{
			User::Leave(KErrArgument);
			}
		User::LeaveIfError(aSqlStatement.BindInt64(
			User::LeaveIfError(aSqlStatement.ParameterIndex(*paramNames[i])), hex));
		}
	}


/**
Set up the CCntSqlStatement objects held by the class.
*/
void C12keyPredictiveSearchTable::ConstructL()
	{
	PRINT(_L("C12keyPredictiveSearchTable::ConstructL"));

	CPplPredictiveSearchTableBase::ConstructL();
	// Set details of INSERT
	iInsertStmnt->SetParamL(KPredSearchContactId,
	                        KPredSearchContactIdParam);
	iInsertStmnt->SetParamL(KPredSearchNameAsNumber,
							KPredSearchNameAsNumberParam);
	iInsertStmnt->SetParamL(KPredSearchNameAsNumber2,
							KPredSearchNameAsNumber2Param);
	iInsertStmnt->SetParamL(KPredSearchNameAsNumber3,
							KPredSearchNameAsNumber3Param);
	iInsertStmnt->SetParamL(KPredSearchNameAsNumber4,
							KPredSearchNameAsNumber4Param);
	iInsertStmnt->SetParamL(KPredSearchFirstName,
							KPredSearchFirstNameParam);
	iInsertStmnt->SetParamL(KPredSearchLastName,
							KPredSearchLastNameParam);

	PRINT(_L("C12keyPredictiveSearchTable::ConstructL create key map"));
	iKeyMap = C12keyKeyMap::NewL();

	PRINT(_L("C12keyPredictiveSearchTable::ConstructL ends"));
	}


/**
Constructor
*/
C12keyPredictiveSearchTable::C12keyPredictiveSearchTable(RSqlDatabase& aDatabase) :
	CPplPredictiveSearchTableBase(aDatabase, KMaxTokens, KMaxTokenLength)
	{
	}


// TODO: maybe move this fn to CPcsKeyMap, and it can use code of GetNumericLimits()

// E.g. aToken = "01230" -> append KPadChar until has KMaxTokenLength characters
// -> "01230ffffffffff" -> convert to hexadecimal number -> 0x01230ffffffffff.
// If this function would leave, causes panic, perhaps because of QString parameter?
quint64 C12keyPredictiveSearchTable::ConvertToHex(QString aToken) const
	{
	if (aToken.length() > KMaxTokenLength)
		{
		return KConversionError;
		}
    QString padded = aToken.leftJustified(KMaxTokenLength, KPadChar);

    TBuf<KMaxTokenLength> log(padded.utf16());
    PRINT1(_L("C12keyPredictiveSearchTable::ConvertToHex padded '%S'"), &log);

	// Replace unmapped char and the following characters with KPadChar.
    QString replaced = padded;
    bool done(false);
    for (TInt i = 0; i < KMaxTokenLength && !done; ++i)
        {
        if (!IsValidChar(padded[i]))
            {
            // replace() does not work, it puts just one KPadChar at end
            // replaced = padded.replace(i, KMaxTokenLength - i, KPadChar);

            padded.remove(i, KMaxTokenLength - i);
            replaced = padded.leftJustified(KMaxTokenLength, KPadChar);
            done = true;

			TBuf<KMaxTokenLength> log2(replaced.utf16());
			PRINT1(_L("After replacing '%S'"), &log2);
            }
        }

    const TInt KHexadecimalBase = 16;
    bool ok(true);
    quint64 hex = replaced.toULongLong(&ok, KHexadecimalBase);
    if (!ok)
        {
		PRINT(_L("conv to hex failed"));
		return KConversionError;
        }
    
    PRINT1(_L("C12keyPredictiveSearchTable::ConvertToHex result 0x%lx"), hex);
    return hex;
	}


const TDesC& C12keyPredictiveSearchTable::GetTableNameL(const QChar aCh) const
	{
	switch (aCh.toAscii())
		{
		case '0': return KSqlContactPredSearchTable0;
		case '1': return KSqlContactPredSearchTable1;
		case '2': return KSqlContactPredSearchTable2;
		case '3': return KSqlContactPredSearchTable3;
		case '4': return KSqlContactPredSearchTable4;
		case '5': return KSqlContactPredSearchTable5;
		case '6': return KSqlContactPredSearchTable6;
		case '7': return KSqlContactPredSearchTable7;
		case '8': return KSqlContactPredSearchTable8;
		case '9': return KSqlContactPredSearchTable9;
		case MAPPED_CHAR_FOR_STAR: return KSqlContactPredSearchTable10;
		case MAPPED_CHAR_FOR_HASH: return KSqlContactPredSearchTable11;
		default:
			PRINT1(_L("C12keyPredictiveSearchTable::GetTableNameL unknown char '%c'"),
				   aCh.toAscii());
			User::Leave(KErrArgument);
			return KNullDesC;
		}
	}
