/*
* 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: Retrieves the character map for each of the numeric keys.
*/

// INCLUDE FILES
#include "cpcskeymap.h"
#include <QChar>
#include <QString>

#if defined(USE_ORBIT_KEYMAP)
#include <hbinputkeymap.h>
#include <hbinputkeymapfactory.h>
#endif // #if defined(USE_ORBIT_KEYMAP)

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

const QChar KSpaceChar = ' ';

// Separator character stored in predictive search table columns
const QChar KSeparatorChar = ' ';


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

// ----------------------------------------------------------------------------
// CPcsKeyMap::~CPcsKeyMap
// ----------------------------------------------------------------------------
CPcsKeyMap::~CPcsKeyMap()
    {
    PRINT(_L("Enter CPcsKeyMap::~CPcsKeyMap"));    
    PRINT(_L("End CPcsKeyMap::~CPcsKeyMap"));
    }

// ----------------------------------------------------------------------------
// CPcsKeyMap::GetMappedStringL
// ----------------------------------------------------------------------------
HBufC* CPcsKeyMap::GetMappedStringL(const TDesC& aSource) const
    {
    PRINT1(_L("Enter CPcsKeyMap::GetMappedStringL input '%S'"), &aSource);

	QString source((QChar*)aSource.Ptr(), aSource.Length());
	QString result;
	TInt err(KErrNone);
	QT_TRYCATCH_ERROR(err, result = GetMappedString(source));
	User::LeaveIfError(err);

    HBufC* destination = HBufC::NewL(result.length());
	destination->Des().Copy(result.utf16());

    PRINT1(_L("End CPcsKeyMap::GetMappedStringL result '%S'"), destination);
    return destination;
    }

// ----------------------------------------------------------------------------
// CPcsKeyMap::GetMappedString
// ----------------------------------------------------------------------------
QString CPcsKeyMap::GetMappedString(QString aSource) const
	{
#if defined(WRITE_PRED_SEARCH_LOGS)
	const int KLogLength = 30;
	TBuf<KLogLength> log(aSource.left(KLogLength).utf16());
	PRINT1(_L("Enter CPcsKeyMap::GetMappedString input '%S'"), &log);
#endif

	QString destination;
	TBool skipHashStar = DetermineSpecialCharBehaviour(aSource);
	TInt length = aSource.length();

    for (int i = 0; i < length; ++i)
        {
        if (aSource[i] == KSpaceChar)
            {
            destination.append(KSeparatorChar);
            }
        else
			{
			QChar ch(0);
#if defined(USE_ORBIT_KEYMAP)
            ch = MappedKeyForChar(aSource[i]);
#else
            ch = UseHardcodedKeyMap(aSource[i]);            
#endif
			if (!ShouldSkipChar(ch, skipHashStar))
				{
				destination.append(ch);
				}
			}
		}
#if defined(WRITE_PRED_SEARCH_LOGS)
	log = destination.left(KLogLength).utf16();
	PRINT1(_L("End CPcsKeyMap::GetMappedString result '%S'"), &log);
#endif
    return destination;
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::GetNumericLimitsL
// In order to speed up the execution, caller should convert search pattern
// with a one call to CPcsKeyMap::GetMappedStringL() and then pass the tokens
// to CPcsKeyMap::GetNumericLimitsL().
// So it is expected that aString contains only certain characters.
// ----------------------------------------------------------------------------
TInt CPcsKeyMap::GetNumericLimits(QString aString,
								  QString& aLowerLimit,
								  QString& aUpperLimit) const
	{
	PRINT(_L("CPcsKeyMap::GetNumericLimits"));
	if (aString.length() > iMaxKeysStoredInDb)
		{
		QString truncated = aString.left(iMaxKeysStoredInDb);
		aString = truncated;
		}

	TInt err = ComputeValue(aString, EFalse, aLowerLimit);
	if (err == KErrNone)
		{
		err = ComputeValue(aString, ETrue, aUpperLimit);
		}
	PRINT1(_L("End CPcsKeyMap::GetNumericLimits ret=%d"), err);
	return err;
	}

#if defined(USE_ORBIT_KEYMAP)
// ----------------------------------------------------------------------------
// CPcsKeyMap::Separator
// ----------------------------------------------------------------------------
QChar CPcsKeyMap::Separator() const
    {
    return KSeparatorChar;
    }

// ----------------------------------------------------------------------------
// CPcsKeyMap::SetHardcodedCharacters
// Default implementation selects only the current default language.
// ----------------------------------------------------------------------------
QList<HbInputLanguage> CPcsKeyMap::SelectLanguages()
	{
	QList<HbInputLanguage> languages;
	HbInputLanguage inputLanguage(QLocale::system().language()); 
	languages << inputLanguage;
	return languages;
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::SetHardcodedCharacters
// Default implementation does nothing
// ----------------------------------------------------------------------------
void CPcsKeyMap::SetHardcodedCharacters()
	{
	}
#endif // #if defined(USE_ORBIT_KEYMAP)

// ----------------------------------------------------------------------------
// CPcsKeyMap::DetermineSpecialCharBehaviour
// Default implementation
// ----------------------------------------------------------------------------
TBool CPcsKeyMap::DetermineSpecialCharBehaviour(QString /*aSource*/) const
	{
	return EFalse;
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::ShouldSkipChar
// Default implementation
// ----------------------------------------------------------------------------
TBool CPcsKeyMap::ShouldSkipChar(QChar /*aChar*/, TBool /*aSkipHashStar*/) const
	{
	return EFalse;
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::ConstructL
// ----------------------------------------------------------------------------
#if defined(USE_ORBIT_KEYMAP)
void CPcsKeyMap::ConstructL(HbKeyboardType aKeyboardType)
#else
void CPcsKeyMap::ConstructL()
#endif
	{
	PRINT(_L("Enter CPcsKeyMap::ConstructL"));

#if defined(USE_ORBIT_KEYMAP)
	TInt err(KErrNone);
	QT_TRYCATCH_ERROR(err,
		{
		InitKeyMappings();
		SetHardcodedCharacters();
		ConstructLanguageMappings(aKeyboardType);
		});
	if (err != KErrNone)
        {
        PRINT1(_L("CPcsKeyMap::ConstructL exception, err=%d"), err);
        User::Leave(err);
        }
#endif

	PRINT(_L("End CPcsKeyMap::ConstructL"));
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::CPcsKeyMap
// ----------------------------------------------------------------------------
#if defined(USE_ORBIT_KEYMAP)
CPcsKeyMap::CPcsKeyMap(TInt aAmountOfKeys,
					   QChar aPadChar,
					   TInt aMaxKeysStoredInDb) :
	iKeyMapping(),
	iAmountOfKeys(aAmountOfKeys),
	iPadChar(aPadChar),
	iMaxKeysStoredInDb(aMaxKeysStoredInDb)
	{
	}
#else // #if defined(USE_ORBIT_KEYMAP)
CPcsKeyMap::CPcsKeyMap(TInt /*aAmountOfKeys*/,
					   QChar /*aPadChar*/,
					   TInt aMaxKeysStoredInDb) :
	iMaxKeysStoredInDb(aMaxKeysStoredInDb)
	{
	}
#endif // #if defined(USE_ORBIT_KEYMAP)

#if defined(USE_ORBIT_KEYMAP)
// ----------------------------------------------------------------------------
// CPcsKeyMap::InitKeyMappings
// Put string for each key into iKeyMapping.
// ----------------------------------------------------------------------------
void CPcsKeyMap::InitKeyMappings()
	{
    PRINT(_L("Enter CPcsKeyMap::InitKeyMappings"));

	for (TInt i = 0; i < iAmountOfKeys; ++i)
        {
        iKeyMapping << QString("");
        }
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::ConstructLanguageMappings
// Fetch keymap for selected languages.
// ----------------------------------------------------------------------------
void CPcsKeyMap::ConstructLanguageMappings(HbKeyboardType aKeyboardType) 
	{
    PRINT(_L("Enter CPcsKeyMap::ConstructLanguageMappings"));

#if defined(WRITE_PRED_SEARCH_LOGS)
    TInt count(0);
#endif

	QList<HbInputLanguage> languages = SelectLanguages();
    PRINT1(_L("build keymap from %d language(s)"), languages.count());

	TInt languageCount = languages.size();
	for (TInt lang = 0; lang < languageCount; ++lang)
		{
        PRINT2(_L("(%d) handle language %d"), lang, languages[lang].language());
		if (IsLanguageSupported(languages[lang].language()))
			{
			PRINT2(_L("Constructing keymap for lang=%d,var=%d"),
				   languages[lang].language(),
				   languages[lang].variant());
			const HbKeymap* keymap =
				HbKeymapFactory::instance()->keymap(languages[lang].language(),
                                                    languages[lang].variant());
			if (keymap)
			    {
				for (TInt key = 0; key < iAmountOfKeys; ++key)
                    {
                    PRINT1(_L("handle key(enum value %d)"), key); // test
                    const HbMappedKey* mappedKey = keymap->keyForIndex(aKeyboardType, key);
					// 12-key: Most languages don't have mapping for EKeyStar, EKeyHash.
					// QWERTY: Different languages have different amount of keys,
					// so mappedKey can be NULL.
                    if (mappedKey)
                        {
						const QString lowerCase = mappedKey->characters(HbModifierNone); // "abc2.."
						const QString upperCase = mappedKey->characters(HbModifierShiftPressed); // "ABC2.."
						const QString charsForKey = lowerCase + upperCase; 
	    
						// Filter out duplicate characters
						for (TInt i = charsForKey.length() - 1; i >= 0 ; --i) 
							{
							QChar ch = charsForKey[i];
							if (!iKeyMapping[key].contains(ch) &&
								!iHardcodedChars.contains(ch))
								{
#if defined(WRITE_PRED_SEARCH_LOGS)
								char ascChar = ch.toAscii();
								TChar logChar(ArrayIndexToMappedChar(key).unicode());
	
								if (ascChar == 0) // ch can't be represented in ASCII
									{
									PRINT2(_L("CPcsKeyMap: map key(%c) <-> char=0x%x"),
									       logChar, ch);
									}
								else
									{
									PRINT3(_L("CPcsKeyMap: map key(%c) <-> char='%c'(0x%x)"),
										   logChar,
										   ascChar,
										   ascChar);
									}
								++count;
#endif // #if defined(WRITE_PRED_SEARCH_LOGS)
								iKeyMapping[key] += ch;
								}
							}
						}
                    }
			    }
			else
                {
                PRINT(_L("CPcsKeyMap::ContructKeyboardMapping keymap not found"));
                }
			}
		}

#if defined(WRITE_PRED_SEARCH_LOGS)
    PRINT1(_L("End CPcsKeyMap::ConstructLanguageMappings keymap has %d chars"), count);
#endif
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::IsLanguageSupported
// ----------------------------------------------------------------------------
TBool CPcsKeyMap::IsLanguageSupported(QLocale::Language aLanguage) const
	{
	return (aLanguage != QLocale::Japanese && aLanguage != QLocale::Chinese);
	}

// ----------------------------------------------------------------------------
// CPcsKeyMap::MappedKeyForChar
// Loop all QStrings of iKeyMapping to find one containing the character.
// If the character is not mapped, use pad character.
// ----------------------------------------------------------------------------
const QChar CPcsKeyMap::MappedKeyForChar(const QChar aChar) const
	{
    for (TInt index = 0; index < iAmountOfKeys; ++index) 
        {
        if (iKeyMapping[index].contains(aChar))
            {
			return ArrayIndexToMappedChar(index);
            }
        }

#if _DEBUG
	TUint ch = aChar.unicode();
	PRINT2(_L("CPcsKeyMap::MappedKeyForChar no mapping for char '%c' (0x%x)"),
		   ch, ch);
#endif
	return iPadChar;
    }
#endif // #if defined(USE_ORBIT_KEYMAP)

// End of file
