/*
* Copyright (c) 2009 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:  Implementation of HWR engine
*
*/

//FEP INCLUDES
#include <AknFepGlobalEnums.h>
#include <aknfeppeninputenums.h>
#include <PtiHwrRecognizer.h>
#include <e32property.h>
#include <AknFepInternalPSKeys.h>
#include <PtiDefs.h>

//USER INCLUDES
#include "peninputfingerhwrengine.h"
#include "peninputfingerhwrdatastore.h"

// CONSTANT DEFINITION HEADER
#include "peninputfingerhwrstoreconstants.h"

// ---------------------------------------------------------------------------
// SplitString()
// ---------------------------------------------------------------------------
//
static TInt SplitString( const TPtrC &aPtr, 
    TUint16 aSeperator, 
    RPointerArray<HBufC>& aStringArray )
	{
	// phrase input mode
    TInt start = 0;
    TInt length = 0;
	
    for ( TInt i = 0; i < aPtr.Length(); i++ )
        {
        if ( aPtr[i] == aSeperator )
            {
            TPtrC segment( aPtr.Ptr() + start, length );
            TRAP_IGNORE( aStringArray.AppendL( segment.AllocL() ) );
            start += ( length + 1 );
            length = 0;
            }
        else
            {
            length++;               
            }
        }

    if ( length )
        {
        TPtrC segm( aPtr.Ptr() + start, length );
        TRAP_IGNORE( aStringArray.AppendL( segm.AllocL() ) );
        }
        
    return aStringArray.Count();
	}

// ---------------------------------------------------------------------------
// Symbian constructor
// ---------------------------------------------------------------------------
//
CAknFepHwrEngine* CAknFepHwrEngine::NewL( CPtiEngine* aPtiEngine, 
    CPeninputFingerHwrDataStore* aOwner )
    {
    CAknFepHwrEngine* self = new ( ELeave ) CAknFepHwrEngine();
    
    CleanupStack::PushL( self );
    self->ConstructL( aPtiEngine, aOwner );
    CleanupStack::Pop( self );//self

    return self;
    }

// ---------------------------------------------------------------------------
// destructor
// ---------------------------------------------------------------------------
//
CAknFepHwrEngine::~CAknFepHwrEngine()
    {
    if( iOwnPtiEngine )
        {
        delete iPtiEngine;
        }
    delete iCustomKeymap;
    
    delete iIdle;
    
    iNeedPermittedRanges.Close();
    }

// ---------------------------------------------------------------------------
// Do recoginize by engine
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::DoRecognizeL( const RArray<TPoint>& aTraceData, 
    RPointerArray<HBufC>& aCandidates )
    {
    if ( !iRecognizer)
        {
        DoIdleConstructL();
        }
        
    TPoint ctrlStrokeEndMark = iOwner->StrokeEndMarkFromControl();
        
    if ( ctrlStrokeEndMark != iRecognizer->StrokeEndMark() )
        {
        ConvertStrokeEndMark( CONST_CAST( RArray<TPoint>&, aTraceData ), 
            iOwner->StrokeEndMarkFromControl(), 
            iRecognizer->StrokeEndMark() );
        iOwner->SetStrokeEndMark(); // make control's stroke end mark info same to engine
        }
    
    aCandidates.ResetAndDestroy();

    TInt primaryCount = iRecognizer->Recognize( aTraceData, aCandidates ); 

    // filter recognized candidate, set start position for all ranges    
    TPtrC ptr( KSeparator );
    
    // remove uncessary aux candidate, including range separator 
    for ( TInt i=0; i < aCandidates.Count(); i++ )
        {
        if ( aCandidates[i]->Compare( KGestureBackspace ) == 0 )
        	{
        	// convert backspace returned by engine, to make sure it display correctly.
            *aCandidates[i] = KDisplayBackspace;
            break;
        	}
        }
        
    // remove uncessary primary candidate
    TInt totallyCount = aCandidates.Count();
    TInt removePos = iTotalCandidateNum;        
    
    for ( TInt i = removePos; i < totallyCount; i++ )
         {
         delete aCandidates[removePos];
         aCandidates.Remove( removePos );
         } 
   
    // not allowing '-' to be the first char in chinese range 
    // not allowing '/' to be the first char in English and Number range 
    TPtrC dashPtr( KDash );
    TPtrC solidusPtr( KSolidus );
    if ( ( iPremaryRange == ERangeNative ) && 
        ( aCandidates[0]->Compare( dashPtr ) == 0 ) )
        {
        *aCandidates[0] = *aCandidates[1];
        *aCandidates[1] = dashPtr;
        }
    else if ( ( ( iPremaryRange == ERangeEnglish ) || 
        ( iPremaryRange == ERangeNumber ) ) 
        && ( aCandidates[0]->Compare( solidusPtr ) == 0 ) )
        {
        *aCandidates[0] = *aCandidates[1];
        *aCandidates[1] = solidusPtr;
        }
    }
    

// ---------------------------------------------------------------------------
// Do predictive using trigger string
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::DoPredictiveL( const TDesC& aTriggerStr, 
    RPointerArray<HBufC>& aPredictives,
    TBool aNextPage )
    {
    // predictive only valid for Chinese
    if ( ( iLanguage != ELangPrcChinese ) &&
        ( iLanguage != ELangTaiwanChinese ) && 
        ( iLanguage != ELangHongKongChinese ) )
    	{
    	return;    	
    	}

    // activate correct pti language according to given language
    if ( !aNextPage )
        {
    	iPtiEngine->SetCandidatePageLength( KPredictiveCountPerPage );
    	
    	aPredictives.ResetAndDestroy();
    	if( !iPtiEngine->SetPredictiveChineseChar( aTriggerStr ) )
    		{
    		return;
    		}
        }
    else
        {
        if ( !iPtiEngine->NextCandidatePage() )
            {
        	return;
            }
        }
        
    TPtrC ptr = iPtiEngine->CandidatePage();
    
    if ( ( iPtiEngine->InputMode() == EPtiEnginePinyinVkb ) || 
        ( iPtiEngine->InputMode() == EPtiEngineStrokeByPhrase ) ||
        ( iPtiEngine->InputMode() == EPtiEngineZhuyinVkb ) )
    	{
    	SplitString( ptr, KSegment, aPredictives );
    	}
    else
        {
        TInt predictiveCandidateNum = ptr.Length();

        for ( TInt i=0; i<predictiveCandidateNum; i++ )
            {
            aPredictives.Append( ptr.Mid( i,1 ).AllocL() );
            }
        }	
    }
    
#ifdef RD_INTELLIGENT_TEXT_INPUT
// ---------------------------------------------------------------------------
// Do predictive for auto complete in English range
// ---------------------------------------------------------------------------
//
 void CAknFepHwrEngine::DoEngPredictiveL( const TDesC& aTriggerStr, 
    RPointerArray<HBufC>& aPredictives)
    {

    aPredictives.ResetAndDestroy();
    CDesCArray* cands = new (ELeave) CDesCArrayFlat(16);
    CleanupStack::PushL(cands);
    iPtiEngine->ClearCurrentWord();
	iPtiEngine->SetCurrentWord(aTriggerStr);
  
    // Get auto complete candidates
    iPtiEngine->GetCandidateListL(*cands);
    for ( TInt i = 1; i < cands->Count(); i++ )
        {
        // Don't show the first predictive
        aPredictives.Append( (*cands)[i].AllocL() );
        }
   
    CleanupStack::PopAndDestroy( cands ); //cands  	
    }
#endif
 
// ---------------------------------------------------------------------------
// Set primary and auxiliary ranges for hwr engine
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetRanges( const RArray<TInt>& aPermittedRanges )
    {
    TRAP_IGNORE( SetRangesL( aPermittedRanges ) );
    }
    
// ---------------------------------------------------------------------------
// Set primary and auxiliary ranges for hwr engine
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetRangesL( const RArray<TInt>& aPermittedRanges )
    {
   	ASSERT( aPermittedRanges.Count() > 0 );

    if ( !iRecognizer )
        {
    	iNeedPermittedRanges.Reset();
    	
    	for ( TInt i = 0; i < aPermittedRanges.Count(); i++ )
    	    {
            iNeedPermittedRanges.Append( aPermittedRanges[i] );
    	    }
    	
    	iNeedSetRange = ETrue;
    	return;    
        }
    else 
    	{
        iPremaryRange = aPermittedRanges[0];
        iRangeCount = aPermittedRanges.Count();

        SetCandidatesMaxCount( KCandidateCount );


        TRecognitionRange range;
        SetRecognitionRange( aPermittedRanges[0], range );
        
    
#ifdef RD_INTELLIGENT_TEXT_INPUT
        if(iPremaryRange == ERangeEnglish)
            {
            iPtiEngine->ActivateLanguageL( ELangEnglish, EPtiEngineQwertyPredictive );
            iPtiEngine->HandleCommandL( EPtiCommandEnableAutoCompletion  );
            }
        else
            {
            iPtiEngine->HandleCommandL( EPtiCommandDisableAutoCompletion );
            
        	switch ( iLanguage )
                {
                case ELangPrcChinese:
                    {
                    if ( iPtiEngine->ActivateLanguageL( iLanguage, EPtiEnginePinyinVkb ) != KErrNone )
                    	{
                    	iPtiEngine->ActivateLanguageL( iLanguage, EPtiEnginePinyin );
                    	}
                    }
                    break;
                case ELangHongKongChinese:
                    {
                    if ( iPtiEngine->ActivateLanguageL( iLanguage, EPtiEngineStrokeByPhrase ) != KErrNone )
                    	{
                    	iPtiEngine->ActivateLanguageL( ELangHongKongChinese, EPtiEngineStroke );
                    	}
                    }
                    break;
                case ELangTaiwanChinese:
                    {
                    if ( iPtiEngine->ActivateLanguageL( iLanguage, EPtiEngineZhuyinVkb ) != KErrNone )
                    	{
                    	iPtiEngine->ActivateLanguageL( ELangTaiwanChinese, EPtiEngineZhuyin );
                    	}
                    }
                    break;
                default:
                    return;
                }            
            }         
#endif //RD_INTELLIGENT_TEXT_INPUT        
        
        iRecognizer->SetRange( range );
        SetCase( iCase );
    	}
    }

    
// ---------------------------------------------------------------------------------------------
// Set case
// ---------------------------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetCase( const TInt aCase )
    {
    iCase = aCase;
    
    if ( !iRecognizer )
        {
    	iNeedSetCase = ETrue;
        }
    else
        {
        // set letter to lower first when LowerCase
	    // set letter to upper first when UpperCase and TextCase
        if ( aCase == ECaseLower )
            {
            iRecognizer->SetFirstLetterOrder( ELowerFirst );
            }
        else
            {
            iRecognizer->SetFirstLetterOrder( EUpperFirst );
            }
        }
    }

// ---------------------------------------------------------------------------------------------
// Set number mode for hwr engine
// ---------------------------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetNumberMode( const TAknEditorNumericKeymap& aNumberMode )
    {
    iNumberMode = aNumberMode;
    
    if ( !iRecognizer )
        {
        iNeedSetNumberMode = ETrue;
        }
    else
        {
        iRecognizer->SetNumberMode( aNumberMode );
        if( aNumberMode !=  EKeymapFromResource )
            {
            ResetCustomKeyMap();
            }
        }
    }

// ---------------------------------------------------------------------------------------------
// Get stroke end mark from hwr engine
// ---------------------------------------------------------------------------------------------
//
TPoint CAknFepHwrEngine::StrokeEndMark() const
    {
    if ( iRecognizer )
        {
        return iRecognizer->StrokeEndMark();
        }
    else
        {
        return TPoint( KDefaultStrokeEndMarkX, KDefaultStrokeEndMarkY );
        }
    }

// ---------------------------------------------------------------------------------------------
// Set primary candidate num for hwr engine
// ---------------------------------------------------------------------------------------------
//
TInt CAknFepHwrEngine::SetPrimaryCandidateNum( const TInt aNum )
    {
    if ( iRecognizer )
        {
        return iRecognizer->SetCandidateNum( aNum );
        }
    else
        {
        return KErrGeneral;
        }
    }
        
// ---------------------------------------------------------------------------------------------
// Set total candidate num that should be shown
// ---------------------------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetCandidatesMaxCount( const TInt aCount )
    {
    iTotalCandidateNum = aCount;
    }
    
// ---------------------------------------------------------------------------------------------
// Set language
// ---------------------------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetLanguageL( const TInt aLanguage )
    {
    if ( ( iLanguage == aLanguage ) ||
         ( aLanguage != ELangPrcChinese && 
          aLanguage != ELangHongKongChinese && 
          aLanguage != ELangTaiwanChinese ) )
        {
        return;
        }
        
    iLanguage = aLanguage;
        
	switch ( iLanguage )
    {
    case ELangPrcChinese:
        {
        if ( iPtiEngine->ActivateLanguageL( iLanguage, EPtiEnginePinyinVkb ) != KErrNone )
        	{
        	iPtiEngine->ActivateLanguageL( iLanguage, EPtiEnginePinyin );
        	}
        }
        break;
    case ELangHongKongChinese:
        if ( iPtiEngine->ActivateLanguageL( iLanguage, EPtiEngineStrokeByPhrase ) != KErrNone )
        	{
        	iPtiEngine->ActivateLanguageL( ELangHongKongChinese, EPtiEngineStroke );
        	}
        break;
    case ELangTaiwanChinese:
        if ( iPtiEngine->ActivateLanguageL( iLanguage, EPtiEngineZhuyinVkb ) != KErrNone )
        	{
        	iPtiEngine->ActivateLanguageL( ELangTaiwanChinese, EPtiEngineZhuyin );
        	}
        break;
    default:
        return;
    }

	iRecognizer = NULL;
	
	if( !iIdle->IsActive() )
	    {
	    iIdle->Start( TCallBack( BackgroundTaskL, this ) );
	    }
    }

// ---------------------------------------------------------------------------
// Set recognition range for hwr engine
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetCustomKeymapL( const TDesC& aKeyMap )
    {
    ResetCustomKeyMap();
    
    iCustomKeymap = HBufC::NewL( aKeyMap.Length() + KNumberString().Length() );
    iCustomKeymap->Des().Copy( KNumberString() );
    iCustomKeymap->Des().Append( aKeyMap );
    }

// ---------------------------------------------------------------------------
// Set recognition range for hwr engine
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::ResetCustomKeyMap()
    {
    delete iCustomKeymap;
    iCustomKeymap = NULL;
    }

// ---------------------------------------------------------------------------------------------
// CAknFepHwrEngine::BackgroundConstructL
// Do background construct.
// ---------------------------------------------------------------------------------------------
//
TBool CAknFepHwrEngine::BackgroundTaskL( TAny* aPtr )
    {
    CAknFepHwrEngine* self = static_cast<CAknFepHwrEngine*>( aPtr );
    self->DoIdleConstructL();
    return EFalse;
    }
    
// ---------------------------------------------------------------------------
// Set hand writing area size
// ---------------------------------------------------------------------------
//
TInt CAknFepHwrEngine::SetInputAreaSize(TSize& aSize)
    {
    return iRecognizer ? iRecognizer->SetInputAreaSize(aSize) : KErrNotFound;     
    }

// ---------------------------------------------------------------------------
// Set hand writing screen size
// ---------------------------------------------------------------------------
//
TInt CAknFepHwrEngine::SetScreenSize(TSize& aSize)    
    {
    return iRecognizer ? iRecognizer->SetScreenSize(aSize) : KErrNotFound;
    }

// ---------------------------------------------------------------------------
// C++ constructor
// ---------------------------------------------------------------------------
//
CAknFepHwrEngine::CAknFepHwrEngine()
    {
    iNeedSetNumberMode = EFalse;
    iNeedSetCase = EFalse;
    iNeedSetRange = EFalse;
    iRecognizer = NULL;    
    iOwnPtiEngine = EFalse;
    }

// ---------------------------------------------------------------------------
// Second phase constructor
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::ConstructL( CPtiEngine* aPtiEngine, 
    CPeninputFingerHwrDataStore* aOwner )
    {
    if( !aPtiEngine )
        {
        iPtiEngine = CPtiEngine::NewL( ETrue );
        iOwnPtiEngine = ETrue;
        }
    else
    	{
    	iPtiEngine = aPtiEngine;
    	}

    iIdle = CIdle::NewL( CActive::EPriorityIdle );
    iOwner = aOwner;
    }

// ---------------------------------------------------------------------------
// Set recognition range for hwr engine
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetRecognitionRange( const TInt aRange, 
    TRecognitionRange& aRecognitionRange )
    {
    aRecognitionRange.iLanguage = TLanguage( iLanguage );

    switch ( aRange )
        {
        case ERangeNative:
			{
            if ( iLanguage == ELangPrcChinese )
                {
                aRecognitionRange.iSubRange = EPtiRangePRCChinese;
                }
            else if ( iLanguage == ELangHongKongChinese )
                {
                aRecognitionRange.iSubRange = EPtiRangeHKChinese;
                }
            else
            	{
                aRecognitionRange.iSubRange = EPtiRangeTWChinese;
                }
			}
            break;
        case ERangeEnglish:
            {
            aRecognitionRange.iLanguage = ELangEnglish;
            aRecognitionRange.iSubRange = EPtiRangeLatin;  
            }
            break;
        case ERangeNumber:
            {
            aRecognitionRange.iSubRange = EPtiRangeNumber;
            }
            break;
        default:
            break;
        }
    }
    
// ---------------------------------------------------------------------------------------------
// CAknFepHwrEngine::DoIdleConstructL
// Do background construct.
// ---------------------------------------------------------------------------------------------
//
void CAknFepHwrEngine::DoIdleConstructL()
    {
    if ( iRecognizer )
        {
        return;
        }

	iRecognizer = iPtiEngine->GetHwrRecognizerL( TLanguage( iLanguage ) );
    
	iOwner->SetStrokeEndMark();
	SetPrimaryCandidateNum( KPremaryCandidateCount );
	
    if ( iNeedSetRange )
        {
        SetRanges( iNeedPermittedRanges );
        iNeedPermittedRanges.Reset();
        iNeedSetRange = EFalse;        
        }
    
    if ( iNeedSetCase )
        {
    	SetCase( iCase );
    	iNeedSetCase = EFalse;
        }
    
    if ( iNeedSetNumberMode )
        {
    	SetNumberMode( TAknEditorNumericKeymap( iNumberMode ) );
    	iNeedSetNumberMode = EFalse;
        }
    }

// ---------------------------------------------------------------------------
// Convert stroke end mark
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::ConvertStrokeEndMark( RArray<TPoint>& aTraceData, 
    TPoint aPnt1, TPoint aPnt2 )
    {
    TInt count = aTraceData.Count();

    for ( TInt i = 0; i < count; i++ )
        {
    	if ( aTraceData[i] == aPnt1 )
    	    {
    		aTraceData.Remove( i );
    		aTraceData.Insert( aPnt2, i );
    	    }
        }
    }
    
// ---------------------------------------------------------------------------
// Reset Keyboard type to original type
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::ResetKeyboardType()
    {
#ifdef RD_INTELLIGENT_TEXT_INPUT    
    RProperty::Set( KPSUidAknFep, KAknFepVirtualKeyboardType, iKeyboardType );
#endif
    }        
     
// ---------------------------------------------------------------------------
// Set Keyboard type to Qwerty
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::SetKeyboardToQwerty()
    {
#ifdef RD_INTELLIGENT_TEXT_INPUT   
    RProperty::Set( KPSUidAknFep, KAknFepVirtualKeyboardType, EPtiKeyboardQwerty4x12 );
#endif
    }        

// ---------------------------------------------------------------------------
// Get Keyboard type
// ---------------------------------------------------------------------------
//
void CAknFepHwrEngine::GetKeyboardType()
    {
#ifdef RD_INTELLIGENT_TEXT_INPUT
    RProperty::Get( KPSUidAknFep, KAknFepVirtualKeyboardType, iKeyboardType );
#endif    
    }        

     
       
//End Of File
