/*
* Copyright (c) 2002-2004 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 FILES
#include "SortUtilJapan.h"
#include "SortUtilImpl.h"
#include "SortUtilImplExport.h"
#include <jplangutil.h>

// Unnamed namespace for local definitions
namespace
    {

    /**
     * Character classes for Japanese sorting, in sorting order.
     */
    enum TJapaneseCharClass
        {
        ECharClassKana,
        ECharClassLatin,
        ECharClassDigit,
        ECharClassKanji,
        ECharClassSpecial
        };

    /**
     * Returns the Japanese character class of aChar.
     */
    TJapaneseCharClass CharClass(TUint aChar)
        {
        const TChar ch(aChar);
        // Special characters
        if (ch.IsSpace() || ch.IsPunctuation() || ch.IsControl()
            || aChar >= 0xF0000 && aChar <= 0xFFFD)
            {
            return ECharClassSpecial;
            }
        // Digit
        else if (aChar >= '0' && aChar <= '9' ||
            aChar >= 0xFF10 && aChar <= 0xFF19)
            {
            return ECharClassDigit;
            }
        // Latin
        else if (aChar >= 'A' && aChar <= 'Z' ||
            aChar >= 'a' && aChar <= 'z' ||
            aChar >= 0x00C0 && aChar <= 0x1EF3 && aChar != 0x00D7 && aChar != 0x00F7 ||
            aChar >= 0xFF21 && aChar <= 0xFF3A ||
            aChar >= 0xFF41 && aChar <= 0xFF5A)
            {
            return ECharClassLatin;
            }
        // Kana
        else if (JPLangUtil::IsKatakana(static_cast<TText>(aChar)) ||
            JPLangUtil::IsHiragana(static_cast<TText>(aChar)))
            {
            return ECharClassKana;
            }
        // Kanji
        else if (JPLangUtil::IsKanji(static_cast<TText>(aChar)))
            {
            return ECharClassKanji;
            }
        // All other charcters are "special"
        else
            {
            return ECharClassSpecial;
            }
        }

    /**
     * Compares two characters with Japanese comparison rules.
     */
    TInt JapaneseCompareC(TUint aLhs, TUint aRhs)
        {
        // First compare character classes
        const TInt lhsCls = CharClass(aLhs);
        const TInt rhsCls = CharClass(aRhs);
        return lhsCls - rhsCls;
        }

    /**
     * Compares two strings with Japanese comparison rules.
     */
    TInt JapaneseCompareS(const TDesC& aLhs, const TDesC& aRhs)
        {
        TInt result = 0;

        const TInt minLen = Min(aLhs.Length(), aRhs.Length());
        TInt i;
        TInt prevCompPos(0);
        for (i = 0; i < minLen && result == 0; ++i)
            {
            result = JapaneseCompareC(aLhs[i], aRhs[i]);
            if (result == 0)
                {
                TInt orgClass = CharClass(aLhs[i]);
                TPtrC lPart;
                TPtrC rPart;
                if (orgClass == ECharClassKana)
                    {
                    int j = i;
                    for (; j < aLhs.Length(); ++j)
                        {
                        if (orgClass != CharClass(aLhs[j]))
                            {
                            break;
                            }
                        }
                    lPart.Set(aLhs.Mid(prevCompPos, j - prevCompPos));
                    for (j = i; j < aRhs.Length(); ++j)
                        {
                        if (orgClass != CharClass(aRhs[j]))
                            {
                            break;
                            }
                        }
                    rPart.Set(aRhs.Mid(prevCompPos, j - prevCompPos));
                    result = lPart.CompareC(rPart);
                    if (result != 0)
                        {
                        const TInt minKanaLen = Min(lPart.Length(), rPart.Length());
                        TPtrC lKanaPart(lPart.Left(minKanaLen));
                        TPtrC rKanaPart(rPart.Left(minKanaLen));
                        TInt resultKana = lKanaPart.MatchC(rKanaPart);
                        if (resultKana == 0)
                            {
                            TInt maxKanaLen = i+minKanaLen;
                            if (i+minKanaLen+1 >= minLen)
                                {
                                if (maxKanaLen < aLhs.Length()
                                 || maxKanaLen < aRhs.Length())
                                    {
                                    result = 0;
                                    i += (minKanaLen-1);
                                    }
                                else
                                    {
                                    result = lKanaPart.CompareC(rKanaPart);;
                                    }
                                }
                            else
                                {
                                result = 0;
                                i += (minKanaLen-1);
                                }
                            }
                        }

                    }
                else
                    {
                    lPart.Set(aLhs.Mid(i, 1));
                    rPart.Set(aRhs.Mid(i, 1));
                    result = lPart.CompareC(rPart);
                    }
                }
            else
                {
                prevCompPos = i;
                }
            }
        if (result == 0)
            {
            if (i < aLhs.Length())
                {
                // aLhs is longer than aRhs
                result += User::Collate(aLhs[i]);
                }
            if (i < aRhs.Length())
                {
                // aRhs is longer that aLhs
                result -= User::Collate(aRhs[i]);
                }
            }
        return result;
        }
    }  // namespace

// Only export in DLL
EXPORT_C MSortUtil* SortUtilFactoryFunctionL()
    {
    MSortUtil* util = new (ELeave) TSortUtilJapan;
    return util;
    }

inline TSortUtilJapan::TSortUtilJapan()
    {
    }

TSortUtilJapan::~TSortUtilJapan()
    {
    }

TInt TSortUtilJapan::CompareItems
        (const MSortKeyArray& aLhs,
        const MSortKeyArray& aRhs) const
    {
    TInt result(0);
    TInt lhsIndex(0);
    TInt rhsIndex(0);

    // Compare only pronounciation keys in the first pass
    do
        {
        const TDesC& lhsText =
            SortUtilImpl::FindNextNonEmptyKey(aLhs, ESortKeyPronounciation, lhsIndex);
        const TDesC& rhsText =
            SortUtilImpl::FindNextNonEmptyKey(aRhs, ESortKeyPronounciation, rhsIndex);
        if (lhsText.Length() > 0)
            {
            // lhs has pronounciation key
            if (rhsText.Length() > 0)
                {
                // Both lhs and rhs have pronounciation key: compare the key texts
                result = JapaneseCompareS(lhsText, rhsText);
                }
            else
                {
                // Only lhs has pronounciation key
                result = -1;
                }
            }
        else
            {
            // lhs does not have pronounciation key
            if (rhsText.Length() > 0)
                {
                // Only rhs has pronounciation key
                result = 1;
                }
            }

        if (result != 0)
            {
            return result;
            }
        }
    while (lhsIndex <= aLhs.SortKeyCount() && rhsIndex < aRhs.SortKeyCount());

    // No difference found with pronounciation keys: compare basic keys
    lhsIndex = 0;
    rhsIndex = 0;
    do
        {
        const TDesC& lhsText =
            SortUtilImpl::FindNextNonEmptyKey(aLhs, ESortKeyBasic, lhsIndex);
        const TDesC& rhsText =
            SortUtilImpl::FindNextNonEmptyKey(aRhs, ESortKeyBasic, rhsIndex);
        result = JapaneseCompareS(lhsText, rhsText);
        if (result != 0)
            {
            return result;
            }
        }
    while (lhsIndex <= aLhs.SortKeyCount() && rhsIndex < aRhs.SortKeyCount());

    return result;
    }

// End of file
