/*
* Copyright (c) 2002-2005 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 the vkb data manager
*
*/


// system includes


// user includes
#include "AknFepVkbPinyinAnalyser.h"
#include "AknFepVkbPinyinAnalyserDb.h"

class TSpellBand
{
public:
    int iStart;
    int iCount;
}; 


class TSpellAnalysis : public TPriQueLink  
{
public:
    RArray<TInt> iSeparators;
    TSpellAnalysis()
    {
        iPriority = -0x10000;
    }
    ~TSpellAnalysis()
        {
        iSeparators.Close();
        }
        
    TSpellAnalysis(const TSpellAnalysis& aSource)
    {
        iPriority = aSource.iPriority;
        for( TInt i = 0; i < aSource.iSeparators.Count(); ++i )
        {
            iSeparators.Append(aSource.iSeparators[i]);
        }
    }
    void Calculate()
    {
        iPriority = (iSeparators.Count() + 1) << 16;
        TInt multi = 12;
        for( TInt i = 0; i < iSeparators.Count(); ++i )
        {
            iPriority += ( 6 - iSeparators[i]) << multi; 
            if( multi >= 3 )
            {
                multi -= 3;
            }            
        }
        iPriority = -iPriority;
    }
    TInt GetStartPos()
    {
        if( iSeparators.Count() == 0)
        {
            return 0;
        }
        else
        {
            return iSeparators[ iSeparators.Count() - 1 ];
        }
    }
    void AddSeparator(TInt aPos)
    {
        iSeparators.Append(aPos);
        Calculate();
    }
};

// -----------------------------------------------------------------------------
// CAknFepVkbPinyinAnalyser::CAknFepVkbPinyinAnalyser
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CAknFepVkbPinyinAnalyser::CAknFepVkbPinyinAnalyser()
    {
    iAnalysisFlag = EFalse;
    }
       
// -----------------------------------------------------------------------------
// CAknFepVkbPinyinAnalyser::NewL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CAknFepVkbPinyinAnalyser* CAknFepVkbPinyinAnalyser::NewL( CPinyinAnalyserDbFeed* aDb )
    {
    CAknFepVkbPinyinAnalyser* self = new ( ELeave ) CAknFepVkbPinyinAnalyser();
    CleanupStack::PushL(self);
    self->ConstructL( aDb ); 
    CleanupStack::Pop();
    return self;
    }  
      
// -----------------------------------------------------------------------------
// CAknFepVkbPinyinAnalyser::~CAknFepVkbPinyinAnalyser
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CAknFepVkbPinyinAnalyser::~CAknFepVkbPinyinAnalyser()
    {
    delete []iParagraph;
    delete []iSpellLibrary;
    delete iLastResult;
    delete iDb;
    }


void CAknFepVkbPinyinAnalyser::ConstructL( CPinyinAnalyserDbFeed* aDb )
    {
    ASSERT( aDb != NULL );
    iDb = aDb;

    TInt length = iDb->GetSpellCount();
    
    iSpellLibrary = new(ELeave) TPtrC[length];
    for(TInt i = 0; i < length; ++i )
        {
        iSpellLibrary[i].Set( aDb->GetSpellByIndex( i ) );
        }
        
    TInt current = 0;
    TInt pinyinElementCount = iDb->GetSpellElementCount();
    iParagraph = new(ELeave) TSpellBand[pinyinElementCount];
    iParagraph[current].iStart = 0;
    for(TInt i = 0; i < length; ++i )
        {
        if( iSpellLibrary[i][0] != iDb->GetSpellElementByIndex( current ) )
            {
            iParagraph[current].iCount = i - iParagraph[current].iStart;
            current++;
            iParagraph[current].iStart = i;
            while( iSpellLibrary[i][0] != iDb->GetSpellElementByIndex( current ) )
                {
                iParagraph[current].iCount = 0;
                current ++;
                iParagraph[current].iStart = i;
                }
            }
        }
        
    iParagraph[current].iCount = length - iParagraph[current].iStart;
    
    }
 
    
HBufC* CAknFepVkbPinyinAnalyser::LastAnalysisResult()
    {
    return iLastResult;
    }

TBool CAknFepVkbPinyinAnalyser::AnalyzeL(const TDesC& aInputString)
    {
    delete iLastResult;
    iLastResult = NULL;
    
    iLastResult = HBufC::NewL(aInputString.Length() * 2);
    
    if( aInputString.Length() < 1 )
    {
        return ETrue;
    }
    
    if( aInputString.Length() == 1 )
        {
        iLastResult->Des().Append( aInputString[0] );
        if ( iDb->IsFirstElement( aInputString[0] )  )
            {
            return ETrue;
            }
        else 
            {
            return EFalse;
            }
        }
    
    int first = 0;
    int second = 1;
    int previous = 0;
    iAnalysisFlag = ETrue;
    //rough spilit
    for(; second < aInputString.Length(); first++, second++ )
        {
        if( !iDb->IsNormalElement(aInputString[first]) )
            {
            //go to detail analysis
            if( first > previous)
                {
                TPtrC toanalysis = aInputString.Mid(previous, first - previous);
                DetailAnalyse(toanalysis);
                }
                
            iLastResult->Des().Append(aInputString[first]);
            previous = first + 1;
            }
        else if(!iDb->IsNormalElement(aInputString[second]) )
            {
            //go to detail analysis
            if( second > previous)
                {
                TPtrC toanalysis = aInputString.Mid(previous, second - previous);
                DetailAnalyse(toanalysis);
                }
                
            iLastResult->Des().Append(aInputString[second]);
            previous = second + 1;
            first ++;
            second ++;
            }                    
        else if( iDb->GetSpellElementType( aInputString[first]) == ESpellVowel || 
                 iDb->GetSpellElementType( aInputString[second]) == ESpellVowel)
            {
            continue;
            }
        else if( iDb->GetSpellElementType( aInputString[first]) == ESpellConsonant || 
                 iDb->GetSpellElementType( aInputString[second]) == ESpellConsonant  )
            {
            //go to detail analysis
            TPtrC toanalysis = aInputString.Mid(previous, second - previous);
            DetailAnalyse(toanalysis);
            iLastResult->Des().Append(KProgramSeparator);
            previous = second;
            }
        else 
            {
            if( (aInputString[second] == L'h' && ( aInputString[first] == L'z' || 
                 aInputString[first] == L'c' || aInputString[first] == L's')) || //ch, sh, zh
                (aInputString[second] == L'g' && aInputString[first] == L'n') || //ng
                (aInputString[second] == L'n' && aInputString[first] == L'h') //hn 
               )
                {
                }
            else 
                {
                //go to detail analysis
                TPtrC toanalysis = aInputString.Mid(previous, second - previous);
                DetailAnalyse(toanalysis);
                iLastResult->Des().Append(KProgramSeparator);
                previous = second;
                }
            }
        }
        
        if( aInputString.Length() <= previous || second > aInputString.Length()  )
            {
            return iAnalysisFlag;
            }
   
        TPtrC toanalysis = aInputString.Mid(previous, second - previous);
        DetailAnalyse(toanalysis);
        return iAnalysisFlag;
    }
    
TSpellAnalysis* CreateTSpellAnalysisL()
    {
    return new(ELeave) TSpellAnalysis();
    }
TBool CAknFepVkbPinyinAnalyser::DetailAnalyse(const TDesC& aInputString)
{
    TPriQue<TSpellAnalysis> open;
    TSpellAnalysis* p = NULL;
    TRAP_IGNORE( p = CreateTSpellAnalysisL());
    
    open.Add(*p);

    int sameLength;
    int startPos;
    TBool findRes;
    TSpellAnalysis* pNext = NULL;
    
    while( !open.IsEmpty() )
    {
        pNext = open.First();
        pNext->Deque();
        
        startPos = pNext->GetStartPos();
        findRes = FindSpell(aInputString.Mid(startPos), sameLength);
        
        if( findRes || sameLength + startPos == aInputString.Length())
        {
            //got it!
            int start = 0;
            for( TInt j = 0; j < pNext->iSeparators.Count(); ++j )
                {
                iLastResult->Des().Append(aInputString.Mid(start, pNext->iSeparators[j] - start));
                start = pNext->iSeparators[j];
                iLastResult->Des().Append(KProgramSeparator);
                }
                
            iLastResult->Des().Append(aInputString.Mid(start, aInputString.Length() - start));
            break;
        }
        else if( sameLength == 0 )
        {
            delete pNext;
            pNext = NULL;
        }
        else
        {
            int partLength = sameLength - 1;
            while( partLength > 0 )
            {
                int templen;
                if( FindSpell(aInputString.Mid(startPos, partLength), templen) )
                {
                    TSpellAnalysis* q = new TSpellAnalysis(*pNext);
                    q->AddSeparator(startPos + partLength);
                    open.Add(*q);
                }
                partLength --;
            }
            pNext->AddSeparator(startPos + sameLength);
            open.Add(*pNext);
        }
    }
    if( pNext )
    {
        delete pNext;
        pNext = NULL;
        while( !open.IsEmpty() )
        {
            TSpellAnalysis* p = open.First();
            p->Deque();
            delete p;
        }
        return true;
    }
    else
    {
    //not found
    /*
        FindSpell(aInputString, sameLength);
        int offset = 0;
        TBool rtn = sameLength > 0;
        while( sameLength > 0 )
        {
            iLastResult->Des().Append(aInputString.Mid(offset, sameLength));
            iLastResult->Des().Append(KProgramSeparator);
            offset += sameLength;
            FindSpell(aInputString.Mid(offset), sameLength);
        }
      */
        //FindSpell(aInputString, sameLength);
        int offset = 0;

        while( offset < aInputString.Length() )
        {
        FindSpell(aInputString.Mid(offset), sameLength);
        
        if( sameLength )
            {
            iLastResult->Des().Append(aInputString.Mid(offset, sameLength));
            iLastResult->Des().Append(KProgramSeparator);
            offset += sameLength;
            }
        else
            {
            iLastResult->Des().Append(aInputString.Mid(offset, 1));
            
            iLastResult->Des().Append(KProgramSeparator);
            
            offset += 1;
            }
        }
        if( iLastResult->Des()[ iLastResult->Length() - 1 ] == KProgramSeparator )
            {
            iLastResult->Des().SetLength( iLastResult->Length() - 1 );
            }
        iAnalysisFlag = EFalse;
        return EFalse;
    }
}

TBool CAknFepVkbPinyinAnalyser::FindSpell(const TDesC& aSpell, TInt& aSameLength)
{
    int index = aSpell[0] - iDb->GetSpellElementByIndex( 0 );   

    int start = iParagraph[index].iStart;
    int end = iParagraph[index].iCount + iParagraph[index].iStart - 1;
    int mid = ( start + end ) >> 1;
    int result = 0;
    aSameLength = 0;
    while( mid > start && mid < end )
    {
        result = Compare(iSpellLibrary[mid], aSpell, aSameLength);
        if( result == 0 )
        {
            return true;
        }
        else if( result < 0 )
        {
            start = mid;
            mid = (start + end ) >> 1;
        }
        else
        {
            end = mid;
            mid = (start + end) >> 1;
        }
    }
    int newLength;
    if( result > 0 )
    {
        result = Compare(iSpellLibrary[start], aSpell, newLength);
    }
    else
    {
        result = Compare(iSpellLibrary[end], aSpell, newLength);
    }
    newLength > aSameLength ? aSameLength = newLength : aSameLength;

    return result == 0;
}

TInt CAknFepVkbPinyinAnalyser::Compare(const TDesC& aFirst, const TDesC& aSecond, 
                                       TInt& aSameLength)
    {
    aSameLength = 0;
    
    TInt length = aFirst.Length() > aSecond.Length() ? aSecond.Length() : aFirst.Length();
    
    for( TInt i = 0; i < length; ++i )
        {
        if( aFirst[i] != aSecond[i] )
            {
            return aFirst[i] - aSecond[i];
            }
            
        aSameLength ++;
        }
        
    return aFirst.Length() - aSecond.Length();
    }

// End of File
