/*
* Copyright (c) 2008 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:  smiely engine class
*
*/

//#include <streamlogger.h>

#include "smileymanager.h"
#include "smileymodel.h"

const TText KSpace = ' ';
const TInt KLinkIndexMark = 0x8000;

// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// CSmileyInfo::CSmileyInfo
// ---------------------------------------------------------------------------
//
CSmileyInfo::CSmileyInfo()
    {    
    }

CSmileyInfo::~CSmileyInfo()
    {
    iStrArray.Reset();
    iStrArray.Close();
    }

void CSmileyInfo::Reset()
    {
    iImageInfo.Reset();
    iStrArray.Reset();
    }

void CSmileyInfo::SetSmileyText( const TDesC& aText )
    {
    TInt startIndex( KErrNotFound );
    for ( TInt i( 0 ); i <= aText.Length(); i++ )
        {
        if ( ( i == aText.Length() || aText[i] == KSpace ) && 
            startIndex != KErrNotFound )
            {
            iStrArray.Append( aText.Mid( startIndex, i - startIndex ) );
            startIndex = KErrNotFound;
            }
        if ( startIndex == KErrNotFound && i < aText.Length() && 
            aText[i] != KSpace )
            {
            startIndex = i;
            }
        }
    }
    
// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// TImageInfo::TImageInfo
// ---------------------------------------------------------------------------
//
TImageInfo::TImageInfo() : iCode( 0 ), iBmpId( 0 ), iMaskId( 0 ),
    iIsAnimation( EFalse ), iStaticBmpId( 0 ), iStaticMaskId( 0 )
    {    
    }

void TImageInfo::Reset()
    {
    iCode = iBmpId = iMaskId = iStaticBmpId = iStaticMaskId = 0;
    iIsAnimation = EFalse;
    }

// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// CSmileyModel::TLinkToImage::TLinkToImage
// ---------------------------------------------------------------------------
//
CSmileyModel::TLinkToImage::TLinkToImage() : iStrLength( 0 ),
    iStrNode( KInvalidIndex ), iImageIndex( KInvalidIndex )
    {    
    }
    
// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// CSmileyModel::TStrNode::TStrNode
// ---------------------------------------------------------------------------
//
CSmileyModel::TStrNode::TStrNode() : iCode(0), iSibling( KInvalidIndex ), 
    iChild( KInvalidIndex ), iParent( KInvalidIndex )
    {    
    }

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

// ---------------------------------------------------------------------------
// CSmileyModel::CSmileyModel
// ---------------------------------------------------------------------------
//
CSmileyModel::CSmileyModel()    
    {    
    }
    
// ---------------------------------------------------------------------------
// CSmileyModel::~CSmileyModel
// ---------------------------------------------------------------------------
//
CSmileyModel::~CSmileyModel()
    {
    Reset();
    iStrArray.Close();
    iImageInfoArray.Close();
    iLinkArray.Close();
    delete iFileName;  
    }

// ---------------------------------------------------------------------------
// CSmileyModel::Reset
// ---------------------------------------------------------------------------
//
void CSmileyModel::Reset()
    {
    iStrArray.Reset();
    iStrArray.Compress();
    iImageInfoArray.Reset();
    iImageInfoArray.Compress();
    iLinkArray.Reset();
    iLinkArray.Compress();
    delete iFileName;
    iFileName = NULL;
    }

// ---------------------------------------------------------------------------
// CSmileyModel::SetSmileyIconFileL
// ---------------------------------------------------------------------------
// 
void CSmileyModel::SetSmileyIconFileL( const TDesC& aIconFileName )
    {
    if ( aIconFileName.Length() > 0 )
        {
        delete iFileName;
        iFileName = NULL;
        iFileName = HBufC::NewL( aIconFileName.Length() );
        TPtr ptr( iFileName->Des() );
        ptr.Copy( aIconFileName );
        }
    }

// ---------------------------------------------------------------------------
// CSmileyModel::SupportedCodesL
// ---------------------------------------------------------------------------
//    
HBufC* CSmileyModel::SupportedCodesL()
    {
    HBufC* codes( NULL );
    if ( iImageInfoArray.Count() > 0 )
        {
        codes = HBufC::NewL( iImageInfoArray.Count() );
        TPtr ptr( codes->Des() );
        for ( TInt i = 0; i < iImageInfoArray.Count(); i++ )
            {
            if ( ptr.Locate( iImageInfoArray[i].iCode ) == KErrNotFound )
                {
                ptr.Append( iImageInfoArray[i].iCode );                                
                }
            }
        }
    return codes;
    }

// ---------------------------------------------------------------------------
// CSmileyModel::GetImageInfo
// ---------------------------------------------------------------------------
//
void CSmileyModel::GetImageInfo( TImageInfo& aImageInfo, HBufC** aFileName )
    {
    TInt count( iImageInfoArray.Count() );
    if ( count == 0 )
        {
        aFileName = NULL;
        aImageInfo.Reset();
        return;
        }
    for ( TInt i = 0; i < count; i++ )
        {
        if ( iImageInfoArray[i].iCode == aImageInfo.iCode )
            {
            aImageInfo = iImageInfoArray[i];
            *aFileName = iFileName;
            }
        }
    }

// ---------------------------------------------------------------------------
// CSmileyModel::AddSimleyL
// ---------------------------------------------------------------------------
//
void CSmileyModel::AddSimleyL( CSmileyInfo& aInfo )
    {
    if ( aInfo.iImageInfo.iCode < CSmileyManager::KSmileyCodeMin || 
        aInfo.iImageInfo.iCode > CSmileyManager::KSmileyCodeMax || 
        aInfo.iStrArray.Count() == 0 )
        {
        return;
        }
    for ( TInt i = 0; i < aInfo.iStrArray.Count(); i++ )
        {
        TInt imageIndex = AddLinkAndImageInfoL( aInfo, i );
        AddStringL( aInfo, imageIndex, i );
        }    
    }

// ---------------------------------------------------------------------------
// CSmileyModel::AddLinkAndImageInfoL
// ---------------------------------------------------------------------------
//
TInt CSmileyModel::AddLinkAndImageInfoL( CSmileyInfo& aInfo, TInt aStrIndex )
    {
    TLinkToImage link;
    for ( TInt i( 0 ); i < iImageInfoArray.Count(); i++ )
        {
        if ( iImageInfoArray[i].iCode == aInfo.iImageInfo.iCode )
            {
            link.iImageIndex = i;
            break;
            }
        }
    if ( link.iImageIndex == KInvalidIndex )
        {
        link.iImageIndex = iImageInfoArray.Count();
        iImageInfoArray.Append( aInfo.iImageInfo );
        }
    link.iStrLength = aInfo.iStrArray[aStrIndex].Length();
    iLinkArray.Append( link );
    return ( iLinkArray.Count() - 1 );
    }

// ---------------------------------------------------------------------------
// CSmileyModel::AddStringL
// ---------------------------------------------------------------------------
//
void CSmileyModel::AddStringL( CSmileyInfo& aInfo, TInt aLinkIndex, 
    TInt aStrIndex )
    {       
    TInt index( KInvalidIndex );    
    for ( TInt i = 0; i < aInfo.iStrArray[aStrIndex].Length(); i++ )
        {        
        TStrNode str; 
        str.iCode = aInfo.iStrArray[aStrIndex][i];
        index = AddStrNodeL( index, str );                               
        }
    iStrArray[index].iChild = ( KLinkIndexMark | aLinkIndex );
    iLinkArray[aLinkIndex].iStrNode = index;    
    }

// ---------------------------------------------------------------------------
// CSmileyModel::AddStrNodeL
// ---------------------------------------------------------------------------
//
TInt CSmileyModel::AddStrNodeL( TInt aIndex, TStrNode& aNode )
    {    
    TInt count = iStrArray.Count();
    if ( count > 0 )
        {        
        TInt child( aIndex == KInvalidIndex ? 0 : 
            iStrArray[aIndex].iChild );
        if ( child != KInvalidIndex )
            {            
            TInt index = FindInSibling( child, aNode.iCode );
            if ( index != KInvalidIndex )
                {
                return index;
                }
            iStrArray[child].iSibling = count;            
            }
        else
            {
            iStrArray[aIndex].iChild = count;
            }
        }
    aNode.iParent = aIndex;
    iStrArray.Append( aNode );
    return count;
    }

// ---------------------------------------------------------------------------
// CSmileyModel::FindInSibling
// ---------------------------------------------------------------------------
//
TInt CSmileyModel::FindInSibling( TInt& aIndex, TText aCode )
    {
    TInt ret( KInvalidIndex );
    if ( iStrArray.Count() > 0 )
        {
        TInt index( aIndex == KInvalidIndex ? 0 : aIndex );
        while ( index != KInvalidIndex )
            {
            if ( iStrArray[index].iCode == aCode )
                {
                ret = index;
                break;
                }
            aIndex = index;
            index = iStrArray[index].iSibling;
            }        
        }
    return ret;
    }

// ---------------------------------------------------------------------------
// CSmileyModel::FindInSibling
// ---------------------------------------------------------------------------
//
TBool CSmileyModel::IsFinalNode( TInt aIndex )
    {
    return ( aIndex != KInvalidIndex && 
        ( iStrArray[aIndex].iChild & KLinkIndexMark ) != 0 );
    }

// ---------------------------------------------------------------------------
// CSmileyModel::FindMatchStr
// ---------------------------------------------------------------------------
//
TInt CSmileyModel::TryFindMatchNode( const TDesC& aText, TInt aTextPos )
    {
    TInt index( 0 );
    index = FindInSibling( index, aText[aTextPos] );
    TInt textPos( aTextPos + 1 );
    while ( index != KInvalidIndex && !IsFinalNode( index ) && 
        textPos < aText.Length() )
        {            
        index = iStrArray[index].iChild;
        index = FindInSibling( index, aText[textPos] );
        if ( index != KInvalidIndex )
            {
            textPos++;
            }
        }
    return index;
    }

// ---------------------------------------------------------------------------
// CSmileyModel::SmileyStringLength
// ---------------------------------------------------------------------------
//
TInt CSmileyModel::SmileyStringLength( TInt aNodeIndex )
    {
    if ( IsFinalNode( aNodeIndex ) )
        {
        TInt linkIndex( iStrArray[aNodeIndex].iChild & ~KLinkIndexMark );
        return iLinkArray[linkIndex].iStrLength;
        }
    return 0;
    }

// ---------------------------------------------------------------------------
// CSmileyModel::SmileyCode
// ---------------------------------------------------------------------------
//
TText CSmileyModel::SmileyCode( TInt aNodeIndex )
    {
    TInt linkIndex( iStrArray[aNodeIndex].iChild & ~KLinkIndexMark );
    TInt imageIndex( iLinkArray[linkIndex].iImageIndex );
    return iImageInfoArray[imageIndex].iCode;
    }

// ---------------------------------------------------------------------------
// CSmileyModel::ReplaceTextWithCodes
// ---------------------------------------------------------------------------
//
void CSmileyModel::ReplaceTextWithCodes( TDes& aText, TInt aDocPos, 
    TInt aNodeIndex )
    {
    TInt linkIndex( iStrArray[aNodeIndex].iChild & ~KLinkIndexMark );
    TInt len( iLinkArray[linkIndex].iStrLength );
    TInt imageIndex( iLinkArray[linkIndex].iImageIndex );
    aText[aDocPos] = iImageInfoArray[imageIndex].iCode;
    aText[aDocPos + 1] = CSmileyManager::KCompensateChar;
    const TInt KThirdIndex = 2;
    for ( TInt i( KThirdIndex ); i < len; i++ )
        {
        aText[i + aDocPos] = CSmileyManager::KPlaceHolder;
        }
    }


/////////////////////////////////////////////////////////////////////////////////////////

const char KStartSymbol[] = {'(', '<', '[', '{'};
const char KEndSymbol[] = {')', '>', ']', '}'};

TInt IndexFrom(const char* const aPtr, TInt aLength, TInt aChar)
    {
    for(TInt i=0; i<aLength; i++)
        {
        if(aPtr[i] == aChar) return i;
        }

    return KErrNotFound;
    }

TBool HavePairedSymbol(const TDesC& aText, TInt aSmileyLength)
    {
    char stack[CSmileyManager::KMaxLength];
    TInt pos = 0;
    
    TInt i = aText.Length() - 1;
    const TInt smileyPos = aText.Length() - aSmileyLength;

    for(TInt i=aText.Length()-1; i>=0; i--)
        {
        TInt index = IndexFrom(KEndSymbol, sizeof(KEndSymbol), aText[i]); // detect end symbol
        if(KErrNotFound != index) // push
            {
            stack[pos++] = KStartSymbol[index];
            }
        else if(pos) // detect start symbol
            {
            if(stack[pos-1] == aText[i]) // meet a start symbol
                {
                pos--;
                if(!pos) return (smileyPos > i);
                }
            }

        if(smileyPos == i) // smiley combination finished
            {
            if(!pos) return EFalse;
            }
        }

    return EFalse;
    }

TBool IsPartOfUrl(const TDesC& aText)
    {
    const TInt end = aText.Length() - 1;
    TInt pos = end;
    while(pos > 0)
        {
        const TUint16 word = aText[--pos];
        if(word == ' ' || word == 0x2029)
            {
            break;
            }
        }
    
    TInt length = end - pos;
    if(length > 5)
        {
        TPtrC field(aText.Mid(pos,length));
        pos = field.Find(_L(":\\\\"));
        if(KErrNotFound == pos) pos = field.Find(_L("://"));
        if(KErrNotFound != pos)
            {
            return ETrue;
            }    
        }
    
    return EFalse;
    }

TBool CSmileyModel::IsSmileyBySemanticAnalysis(const TDesC& aText, TInt aSmileyLength)
    {
    if(HavePairedSymbol(aText, aSmileyLength)) return EFalse;

    if(IsPartOfUrl(aText)) return EFalse;

    return ETrue;
    }
