// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/************************************************************************
 * COPYRIGHT: 
 * Copyright (c) 2000-2016, International Business Machines Corporation
 * and others. All Rights Reserved.
 ************************************************************************/
/************************************************************************
*   Date        Name        Description
*   1/03/2000   Madhu        Creation.
************************************************************************/

#include "unicode/utypes.h"

#if !UCONFIG_NO_TRANSLITERATION

#include "ittrans.h"
#include "transapi.h"
#include "unicode/utypes.h"
#include "unicode/translit.h"
#include "unicode/unifilt.h"
#include "cpdtrans.h"
#include "cmemory.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "unicode/rep.h"
#include "unicode/locid.h"
#include "unicode/uniset.h"

int32_t getInt(UnicodeString str)
{
    char buffer[20];
    int len=str.length();
    if(len>=20) {
        len=19;
    }
    str.extract(0, len, buffer, "");
    buffer[len]=0;
    return atoi(buffer);
}

//---------------------------------------------
// runIndexedTest
//---------------------------------------------

void
TransliteratorAPITest::runIndexedTest(int32_t index, UBool exec,
                                      const char* &name, char* /*par*/) {
    switch (index) {
        TESTCASE(0,TestgetInverse);
        TESTCASE(1,TestgetID);
        TESTCASE(2,TestGetDisplayName);
        TESTCASE(3,TestTransliterate1);
        TESTCASE(4,TestTransliterate2);
        TESTCASE(5,TestTransliterate3);
        TESTCASE(6,TestSimpleKeyboardTransliterator);
        TESTCASE(7,TestKeyboardTransliterator1);
        TESTCASE(8,TestKeyboardTransliterator2);
        TESTCASE(9,TestKeyboardTransliterator3);
        TESTCASE(10,TestGetAdoptFilter);
        TESTCASE(11,TestClone);
        TESTCASE(12,TestNullTransliterator);
        TESTCASE(13,TestRegisterUnregister);
        TESTCASE(14,TestUnicodeFunctor);
        default: name = ""; break;
    }
}


void TransliteratorAPITest::TestgetID() {
    UnicodeString trans="Latin-Greek";
    UnicodeString ID;
    UParseError parseError;
    UErrorCode status = U_ZERO_ERROR;
    Transliterator* t= Transliterator::createInstance(trans, UTRANS_FORWARD, parseError, status);
    if(t==0 || U_FAILURE(status)){
        dataerrln("FAIL: construction of Latin-Greek -  %s",u_errorName(status));
        return;
    }else{
        ID= t->getID();
        if(ID != trans)
            errln("FAIL: getID returned " + ID + " instead of Latin-Greek");
        delete t;
    }
    int i;
    for (i=0; i<Transliterator::countAvailableIDs(); i++){
        status = U_ZERO_ERROR;
        ID = (UnicodeString) Transliterator::getAvailableID(i);
        if(ID.indexOf("Thai")>-1){
            continue;
        }   
        t = Transliterator::createInstance(ID, UTRANS_FORWARD, parseError, status);
        if(t == 0){
            errln("FAIL: " + ID);
            continue;
        }
        if(ID != t->getID())
            errln("FAIL: getID() returned " + t->getID() + " instead of " + ID);
        delete t;
    }
    ID=(UnicodeString)Transliterator::getAvailableID(i);
    if(ID != (UnicodeString)Transliterator::getAvailableID(0)){
        errln("FAIL: calling getAvailableID(index > coundAvailableIDs) should make index=0\n");
    }

    Transliterator* t1=Transliterator::createInstance("Latin-Devanagari", UTRANS_FORWARD, parseError, status);
    Transliterator* t2=Transliterator::createInstance("Latin-Greek", UTRANS_FORWARD, parseError, status);
    if(t1 ==0 || t2 == 0){
        errln("FAIL: construction");
        return;
    }
    Transliterator* t3=t1->clone();
    Transliterator* t4=t2->clone();

    if(t1->getID() != t3->getID() || t2->getID() != t4->getID() || 
       t1->getID() == t4->getID() || t2->getID() == t3->getID() || 
       t1->getID()== t4->getID() )
            errln("FAIL: getID or clone failed");


    Transliterator* t5=Transliterator::createInstance("Latin-Devanagari", UTRANS_FORWARD, parseError, status);
    if(t5 == 0)
        errln("FAIL: construction");
    else if(t1->getID() != t5->getID() || t5->getID() != t3->getID() || t1->getID() != t3->getID())
        errln("FAIL: getID or clone failed");


    delete t1;
    delete t2;
    delete t3;
    delete t4;
    delete t5;
}

void TransliteratorAPITest::TestgetInverse() {
     UErrorCode status = U_ZERO_ERROR;
     UParseError parseError;
     Transliterator* t1    = Transliterator::createInstance("Katakana-Latin", UTRANS_FORWARD, parseError, status);
     Transliterator* invt1 = Transliterator::createInstance("Latin-Katakana", UTRANS_FORWARD, parseError, status);
     Transliterator* t2    = Transliterator::createInstance("Latin-Devanagari", UTRANS_FORWARD, parseError, status);
     Transliterator* invt2 = Transliterator::createInstance("Devanagari-Latin", UTRANS_FORWARD, parseError, status);
     if(t1 == 0 || invt1 == 0 || t2 == 0 || invt2 == 0) {
         dataerrln("FAIL: in instantiation - %s", u_errorName(status));
         return;
     }

     Transliterator* inverse1=t1->createInverse(status);
     Transliterator* inverse2=t2->createInverse(status);
     if(inverse1->getID() != invt1->getID() || inverse2->getID() != invt2->getID()
        || inverse1->getID() == invt2->getID() || inverse2->getID() == invt1->getID() ) 
        errln("FAIL: getInverse() ");

     UnicodeString TransID[]={
       "Halfwidth-Fullwidth",
       "Fullwidth-Halfwidth",
       "Greek-Latin" ,
       "Latin-Greek", 
       //"Arabic-Latin", // removed in 2.0
       //"Latin-Arabic",
       "Katakana-Latin",
       "Latin-Katakana",
       //"Hebrew-Latin", // removed in 2.0
       //"Latin-Hebrew",
       "Cyrillic-Latin", 
       "Latin-Cyrillic", 
       "Devanagari-Latin", 
       "Latin-Devanagari", 
       "Any-Hex",
       "Hex-Any"
     };
     for(uint32_t i=0; i<UPRV_LENGTHOF(TransID); i=i+2){
         Transliterator *trans1=Transliterator::createInstance(TransID[i], UTRANS_FORWARD, parseError, status);
         if(t1 == 0){
           errln("FAIL: in instantiation for" + TransID[i]);
           continue;
         }
         Transliterator *inver1=trans1->createInverse(status);
         if(inver1->getID() != TransID[i+1] )
             errln("FAIL :getInverse() for " + TransID[i] + " returned " + inver1->getID() + " instead of " + TransID[i+1]);
         delete inver1;
         delete trans1;
     }
     delete t1;
     delete t2;
     delete invt1;
     delete invt2;
     delete inverse1;
     delete inverse2;

}

void TransliteratorAPITest::TestClone(){
    Transliterator *t1, *t2, *t3, *t4;
    UErrorCode status = U_ZERO_ERROR;
    UParseError parseError;
    t1=Transliterator::createInstance("Latin-Devanagari", UTRANS_FORWARD, parseError, status);
    t2=Transliterator::createInstance("Latin-Greek", UTRANS_FORWARD, parseError, status);
    if(t1 == 0 || t2 == 0){
        dataerrln("FAIL: construction - %s", u_errorName(status));
        return;
    }
    t3=t1->clone();
    t4=t2->clone();

    if(t1->getID() != t3->getID() || t2->getID() != t4->getID() )
        errln("FAIL: clone or getID failed");
    if(t1->getID()==t4->getID() || t2->getID()==t3->getID() ||  t1->getID()==t4->getID())
        errln("FAIL: clone or getID failed");
     
    delete t1;
    delete t2;
    delete t3;
    delete t4;

}

void TransliteratorAPITest::TestGetDisplayName() {
    UnicodeString dispNames[]= { 
         //ID, displayName
        //"CurlyQuotes-StraightQuotes" ,"CurlyQuotes to StraightQuotes",
          "Any-Hex"                ,"Any to Hex Escape",
          "Halfwidth-Fullwidth"        ,"Halfwidth to Fullwidth" ,
          //"Latin-Arabic"               ,"Latin to Arabic"      ,
          "Latin-Devanagari"           ,"Latin to Devanagari"  ,
          "Greek-Latin"                ,"Greek to Latin"       ,
          //"Arabic-Latin"               ,"Arabic to Latin"      ,
          "Hex-Any"                ,"Hex Escape to Any",
          "Cyrillic-Latin"             ,"Cyrillic to Latin"    ,
          "Latin-Greek"                ,"Latin to Greek"       ,
          "Latin-Katakana"                 ,"Latin to Katakana"        ,
          //"Latin-Hebrew"               ,"Latin to Hebrew"      ,
          "Katakana-Latin"                 ,"Katakana to Latin"        
      };
    UnicodeString name="";
    Transliterator* t;
    UnicodeString message;
    UErrorCode status = U_ZERO_ERROR;
    UParseError parseError;

#if UCONFIG_NO_FORMATTING
    logln("Skipping, UCONFIG_NO_FORMATTING is set\n");
    return;
#else

    for (uint32_t i=0; i<UPRV_LENGTHOF(dispNames); i=i+2 ) {
        t = Transliterator::createInstance(dispNames[i+0], UTRANS_FORWARD, parseError, status);
        if(t==0){
             dataerrln("FAIL: construction: " + dispNames[i+0] + " - " + u_errorName(status));
             status = U_ZERO_ERROR;
             continue;
        }
        t->getDisplayName(t->getID(), name);
        message="Display name for ID:" + t->getID();
      //  doTest(message, name, dispNames[i+1]); //!!! This will obviously fail for any locale other than english and its children!!!
        name=""; 
        t->getDisplayName(t->getID(), Locale::getUS(), name);
        message.remove();
        message.append("Display name for on english locale ID:");
        message.append(t->getID());
    // message="Display name for on english locale ID:" + t->getID();
        doTest(message, name, dispNames[i+1]);
        name="";

        delete t;
    }
#endif

}

void TransliteratorAPITest::TestTransliterate1(){

    UnicodeString Data[]={ 
         //ID, input string, transliterated string
         "Any-Hex",         "hello",    UnicodeString("\\u0068\\u0065\\u006C\\u006C\\u006F", "") ,
         "Hex-Any",         UnicodeString("\\u0068\\u0065\\u006C\\u006C\\u006F", ""), "hello"  ,
         "Latin-Devanagari",CharsToUnicodeString("bha\\u0304rata"), CharsToUnicodeString("\\u092D\\u093E\\u0930\\u0924") ,
         "Latin-Devanagari",UnicodeString("kra ksha khra gra cra dya dhya",""), CharsToUnicodeString("\\u0915\\u094D\\u0930 \\u0915\\u094D\\u0936 \\u0916\\u094D\\u0930 \\u0917\\u094D\\u0930 \\u091a\\u094D\\u0930 \\u0926\\u094D\\u092F \\u0927\\u094D\\u092F") ,

         "Devanagari-Latin",    CharsToUnicodeString("\\u092D\\u093E\\u0930\\u0924"),        CharsToUnicodeString("bh\\u0101rata"),
     //  "Contracted-Expanded", CharsToUnicodeString("\\u00C0\\u00C1\\u0042"),               CharsToUnicodeString("\\u0041\\u0300\\u0041\\u0301\\u0042") ,
     //  "Expanded-Contracted", CharsToUnicodeString("\\u0041\\u0300\\u0041\\u0301\\u0042"), CharsToUnicodeString("\\u00C0\\u00C1\\u0042") ,
         //"Latin-Arabic",        "aap",                                 CharsToUnicodeString("\\u0627\\u06A4")     ,
         //"Arabic-Latin",        CharsToUnicodeString("\\u0627\\u06A4"),                      "aap" 
    };

    UnicodeString gotResult;
    UnicodeString temp;
    UnicodeString message;
    Transliterator* t;
    logln("Testing transliterate");
    UErrorCode status = U_ZERO_ERROR;
    UParseError parseError;

    for(uint32_t i=0;i<UPRV_LENGTHOF(Data); i=i+3){
        t=Transliterator::createInstance(Data[i+0], UTRANS_FORWARD, parseError, status);
        if(t==0){
            dataerrln("FAIL: construction: " + Data[i+0] + " Error: "  + u_errorName(status));
            dataerrln("PreContext: " + prettify(parseError.preContext) + " PostContext: " + prettify( parseError.postContext) );
            status = U_ZERO_ERROR;
            continue;
        }
        gotResult = Data[i+1];
        t->transliterate(gotResult);
        message=t->getID() + "->transliterate(UnicodeString, UnicodeString) for\n\t Source:" + prettify(Data[i+1]);
        doTest(message, gotResult, Data[i+2]);

        //doubt here
        temp=Data[i+1];
        t->transliterate(temp);
        message.remove();
        message.append(t->getID());
        message.append("->transliterate(Replaceable) for \n\tSource:");
        message.append(Data[i][1]);
        doTest(message, temp, Data[i+2]);

        callEverything(t, __LINE__);
        delete t;
    }
}

void TransliteratorAPITest::TestTransliterate2(){
     //testing tranliterate(String text, int start, int limit, StringBuffer result)
   UnicodeString Data2[]={
         //ID, input string, start, limit, transliterated string
         "Any-Hex",         "hello! How are you?",  "0", "5", UnicodeString("\\u0068\\u0065\\u006C\\u006C\\u006F", ""), UnicodeString("\\u0068\\u0065\\u006C\\u006C\\u006F! How are you?", "") ,
         "Any-Hex",         "hello! How are you?",  "7", "12", UnicodeString("\\u0048\\u006F\\u0077\\u0020\\u0061", ""), UnicodeString("hello! \\u0048\\u006F\\u0077\\u0020\\u0061re you?", ""),
         "Hex-Any",         CharsToUnicodeString("\\u0068\\u0065\\u006C\\u006C\\u006F\\u0021\\u0020"), "0", "5",  "hello", "hello! "  ,
       //  "Contracted-Expanded", CharsToUnicodeString("\\u00C0\\u00C1\\u0042"),        "1", "2",  CharsToUnicodeString("\\u0041\\u0301"), CharsToUnicodeString("\\u00C0\\u0041\\u0301\\u0042") ,
         "Devanagari-Latin",    CharsToUnicodeString("\\u092D\\u093E\\u0930\\u0924"), "0", "1",  "bha", CharsToUnicodeString("bha\\u093E\\u0930\\u0924") ,
         "Devanagari-Latin",    CharsToUnicodeString("\\u092D\\u093E\\u0930\\u0924"), "1", "2",  CharsToUnicodeString("\\u0314\\u0101"), CharsToUnicodeString("\\u092D\\u0314\\u0101\\u0930\\u0924")  

    };
    logln("\n   Testing transliterate(String, int, int, StringBuffer)");
    Transliterator* t;
    UnicodeString gotResBuf;
    UnicodeString temp;
    int32_t start, limit;
    UErrorCode status = U_ZERO_ERROR;
    UParseError parseError;

    for(uint32_t i=0; i<UPRV_LENGTHOF(Data2); i=i+6){
        t=Transliterator::createInstance(Data2[i+0], UTRANS_FORWARD, parseError, status);
        if(t==0){
            dataerrln("FAIL: construction: " + prettify(Data2[i+0]) + " - " + u_errorName(status));
            continue;
        }
        start=getInt(Data2[i+2]);
        limit=getInt(Data2[i+3]);
        Data2[i+1].extractBetween(start, limit, gotResBuf);
        t->transliterate(gotResBuf);
        //  errln("FAIL: calling transliterate on " + t->getID());
        doTest(t->getID() + ".transliterate(UnicodeString, int32_t, int32_t, UnicodeString):(" + start + "," + limit + ")  for \n\t source: " + prettify(Data2[i+1]), gotResBuf, Data2[i+4]);

        temp=Data2[i+1];
        t->transliterate(temp, start, limit);
        doTest(t->getID() + ".transliterate(Replaceable, int32_t, int32_t, ):(" + start + "," + limit + ")  for \n\t source: " + prettify(Data2[i+1]), temp, Data2[i+5]);
        status = U_ZERO_ERROR;
        callEverything(t, __LINE__);
        delete t;
        t = nullptr;
    }

    status = U_ZERO_ERROR;
    logln("\n   Try calling transliterate with illegal start and limit values");
    t=Transliterator::createInstance("Any-Hex", UTRANS_FORWARD, parseError, status);
    if(U_FAILURE(status)) {
      errln("Error creating transliterator %s", u_errorName(status));
      delete t;
      return;
    }
    gotResBuf = temp = "try start greater than limit";
    t->transliterate(gotResBuf, 10, 5);
    if(gotResBuf == temp) {
        logln("OK: start greater than limit value handled correctly");
    } else {
        errln("FAIL: start greater than limit value returned" + gotResBuf);
    }

    callEverything(t, __LINE__);
    delete t;

}
void TransliteratorAPITest::TestTransliterate3(){
    UnicodeString rs="This is the replaceable String";
    UnicodeString Data[] = {
        "0",  "0",  "This is the replaceable String",
        "2",  "3",  UnicodeString("Th\\u0069s is the replaceable String", ""),
        "21", "23", UnicodeString("Th\\u0069s is the repl\\u0061\\u0063eable String", ""),
        "14", "17", UnicodeString("Th\\u0069s is t\\u0068\\u0065\\u0020repl\\u0061\\u0063eable String", ""),
    };
    int start, limit;
    UnicodeString message;
    UParseError parseError;
    UErrorCode status = U_ZERO_ERROR;
    Transliterator *t=Transliterator::createInstance("Any-Hex", UTRANS_FORWARD, parseError, status);
    if(U_FAILURE(status)) {
      errln("Error creating transliterator %s", u_errorName(status));
      delete t;
      return;
    }

    if(t == 0)
        errln("FAIL : construction");
    for(uint32_t i=0; i<UPRV_LENGTHOF(Data); i=i+3){
        start=getInt(Data[i+0]);
        limit=getInt(Data[i+1]);
        t->transliterate(rs, start, limit);
        message=t->getID() + ".transliterate(ReplaceableString, start, limit):("+start+","+limit+"):";
        doTest(message, rs, Data[i+2]); 
    }
    delete t;
}

void TransliteratorAPITest::TestSimpleKeyboardTransliterator(){
    logln("simple call to transliterate");
    UErrorCode status=U_ZERO_ERROR;
    UParseError parseError;
    Transliterator* t=Transliterator::createInstance("Any-Hex", UTRANS_FORWARD, parseError, status);
    if(t == 0) {
        UnicodeString context;

        if (parseError.preContext[0]) {
            context += (UnicodeString)" at " + parseError.preContext;
        }
        if (parseError.postContext[0]) {
            context += (UnicodeString)" | " + parseError.postContext;
        }
        errln((UnicodeString)"FAIL: can't create Any-Hex, " +
              (UnicodeString)u_errorName(status) + context);
        return;
    }
    UTransPosition index={19,20,20,20};
    UnicodeString rs= "Transliterate this-''";
    UnicodeString insertion="abc";
    UnicodeString expected=UnicodeString("Transliterate this-'\\u0061\\u0062\\u0063'", "");
    t->transliterate(rs, index, insertion, status);
    if(U_FAILURE(status))
        errln("FAIL: " + t->getID()+ ".translitere(Replaceable, int[], UnicodeString, UErrorCode)-->" + (UnicodeString)u_errorName(status));
    t->finishTransliteration(rs, index);
    UnicodeString message="transliterate";
    doTest(message, rs, expected);

    logln("try calling transliterate with invalid index values");
    UTransPosition index1[]={
        //START, LIMIT, CURSOR
        {10, 10, 12, 10},   //invalid since CURSOR>LIMIT valid:-START <= CURSOR <= LIMIT
        {17, 16, 17, 17},   //invalid since START>LIMIT valid:-0<=START<=LIMIT
        {-1, 16, 14, 16},   //invalid since START<0
        {3,  50, 2,  50}    //invalid since LIMIT>text.length()
    };
    for(uint32_t i=0; i<UPRV_LENGTHOF(index1); i++){
        status=U_ZERO_ERROR;
        t->transliterate(rs, index1[i], insertion, status);
        if(status == U_ILLEGAL_ARGUMENT_ERROR)
            logln("OK: invalid index values handled correctly");
        else
            errln("FAIL: invalid index values didn't throw U_ILLEGAL_ARGUMENT_ERROR throw" + (UnicodeString)u_errorName(status));
    }

    delete t;
}
void TransliteratorAPITest::TestKeyboardTransliterator1(){
    UnicodeString Data[]={
        //insertion, buffer
        "a",   UnicodeString("\\u0061", "")                                           ,
        "bbb", UnicodeString("\\u0061\\u0062\\u0062\\u0062", "")                      ,
        "ca",  UnicodeString("\\u0061\\u0062\\u0062\\u0062\\u0063\\u0061", "")        ,
        " ",   UnicodeString("\\u0061\\u0062\\u0062\\u0062\\u0063\\u0061\\u0020", "") ,
        "",    UnicodeString("\\u0061\\u0062\\u0062\\u0062\\u0063\\u0061\\u0020", "")   ,

        "a",   UnicodeString("\\u0061", "")                                           ,
        "b",   UnicodeString("\\u0061\\u0062", "")                                    ,
        "z",   UnicodeString("\\u0061\\u0062\\u007A", "")                             ,
        "",    UnicodeString("\\u0061\\u0062\\u007A", "")                              

    };
    UParseError parseError;
    UErrorCode status = U_ZERO_ERROR;
    Transliterator* t=Transliterator::createInstance("Any-Hex", UTRANS_FORWARD, parseError, status);
    if(U_FAILURE(status)) {
      errln("Error creating transliterator %s", u_errorName(status));
      delete t;
      return;
    }
    //keyboardAux(t, Data);
    UTransPosition index={0, 0, 0, 0};
    UnicodeString s;
    uint32_t i;
    logln("Testing transliterate(Replaceable, int32_t, UnicodeString, UErrorCode)");
    for (i=0; i<10; i=i+2) {
       UnicodeString log;
       if (Data[i+0] != "") {
           log = s + " + " + Data[i+0] + " -> ";
           t->transliterate(s, index, Data[i+0], status);
           if(U_FAILURE(status)){
               errln("FAIL: " + t->getID()+ ".transliterate(Replaceable, int32_t[], UnicodeString, UErrorCode)-->" + (UnicodeString)u_errorName(status));
           continue;
           }
       }else {
           log = s + " => ";
           t->finishTransliteration(s, index);
       }
       // Show the start index '{' and the cursor '|'
       displayOutput(s, Data[i+1], log, index);
           
    }
    
    s="";
    status=U_ZERO_ERROR;
    index.contextStart = index.contextLimit = index.start = index.limit = 0;
    logln("Testing transliterate(Replaceable, int32_t, char16_t, UErrorCode)");
    for(i=10; i<UPRV_LENGTHOF(Data); i=i+2){
        UnicodeString log;
        if (Data[i+0] != "") {
            log = s + " + " + Data[i+0] + " -> ";
            char16_t c=Data[i+0].charAt(0);
            t->transliterate(s, index, c, status);
            if(U_FAILURE(status)) {
               errln("FAIL: " + t->getID()+ ".transliterate(Replaceable, int32_t[], char16_t, UErrorCode)-->" + (UnicodeString)u_errorName(status));
               continue;
            }
        } else {
           log = s + " => ";
           t->finishTransliteration(s, index);
        }
        // Show the start index '{' and the cursor '|'
        displayOutput(s, Data[i+1], log, index); 
    }

    delete t;
}

void TransliteratorAPITest::TestKeyboardTransliterator2(){
    UnicodeString Data[]={
        //insertion, buffer, index[START], index[LIMIT], index[CURSOR]
        //data for Any-Hex
        "abc",    UnicodeString("Initial String: add-\\u0061\\u0062\\u0063-", ""),                     "19", "20", "20",
        "a",      UnicodeString("In\\u0069\\u0061tial String: add-\\u0061\\u0062\\u0063-", ""),        "2",  "3",  "2" ,
        "b",      UnicodeString("\\u0062In\\u0069\\u0061tial String: add-\\u0061\\u0062\\u0063-", ""), "0",  "0",  "0" ,
        "",       UnicodeString("\\u0062In\\u0069\\u0061tial String: add-\\u0061\\u0062\\u0063-", ""), "0",  "0",  "0" ,
        //data for Latin-Devanagiri
        CharsToUnicodeString("a\\u0304"),     CharsToUnicodeString("Hindi -\\u0906-"),                            "6", "7", "6",
        CharsToUnicodeString("ma\\u0304"),    CharsToUnicodeString("Hindi -\\u0906\\u092E\\u093E-"),              "7", "8", "7",
        CharsToUnicodeString("ra\\u0304"),    CharsToUnicodeString("Hi\\u0930\\u093Endi -\\u0906\\u092E\\u093E-"),"1", "2", "2",
        CharsToUnicodeString(""),       CharsToUnicodeString("Hi\\u0930\\u093Endi -\\u0906\\u092E\\u093E-"),"1", "2", "2"
        //data for contracted-Expanded
     //   CharsToUnicodeString("\\u00C1"), CharsToUnicodeString("Ad\\u0041\\u0301d here:"),             "1",  "2",  "1" ,
     //   CharsToUnicodeString("\\u00C0"), CharsToUnicodeString("Ad\\u0041\\u0301d here:\\u0041\\u0300"), "11", "11", "11",
     //   "",     CharsToUnicodeString("Ad\\u0041\\u0301d here:\\u0041\\u0300"), "11", "11", "11",
    };
    Transliterator *t;
    UnicodeString rs;
    UnicodeString dataStr;
    logln("Testing transliterate(Replaceable, int32_t, UnicodeString, UErrorCode)");       
    UErrorCode status = U_ZERO_ERROR;
    UParseError parseError;
    rs="Initial String: add--";
    t=Transliterator::createInstance("Any-Hex", UTRANS_FORWARD, parseError, status);
    if(t == 0)
        dataerrln("FAIL : construction - %s", u_errorName(status));
    else {
        keyboardAux(t, Data, rs, 0, 20);
        delete t;
    }

    rs="Hindi --";
    t=Transliterator::createInstance("Latin-Devanagari", UTRANS_FORWARD, parseError, status);
    if(t == 0)
        dataerrln("FAIL : construction - %s", u_errorName(status));
    else
        keyboardAux(t, Data, rs, 20, 40);


  //  rs="Add here:";
 //   t=Transliterator::createInstance("Contracted-Expanded");
 //   keyboardAux(t, Data, rs, 35, 55);


    delete t;
}

void TransliteratorAPITest::TestKeyboardTransliterator3(){
    UnicodeString s="This is the main string";
    UnicodeString Data[] = {
        "0", "0", "0",  "This is the main string",
        "1", "3", "2",  UnicodeString("Th\\u0069s is the main string", ""),
        "20", "21", "20",  UnicodeString("Th\\u0069s is the mai\\u006E string", "")
    };

    UErrorCode status=U_ZERO_ERROR;
    UParseError parseError;
    UTransPosition index={0, 0, 0, 0};
    logln("Testing transliterate(Replaceable, int32_t, UErrorCode)");
    Transliterator *t=Transliterator::createInstance("Any-Hex", UTRANS_FORWARD, parseError, status);
    if(t == 0 || U_FAILURE(status)) {
      errln("Error creating transliterator %s", u_errorName(status));
      delete t;
      return;
    }
    for(uint32_t i=0; i<UPRV_LENGTHOF(Data); i=i+4){
        UnicodeString log;
        index.contextStart=getInt(Data[i+0]);
        index.contextLimit=index.limit=getInt(Data[i+1]);
        index.start=getInt(Data[i+2]);
        t->transliterate(s, index, status);
        if(U_FAILURE(status)){
           errln("FAIL: " + t->getID()+ ".transliterate(Replaceable, int32_t[], UErrorCode)-->" + (UnicodeString)u_errorName(status));
           continue;
        }
        t->finishTransliteration(s, index);
        log = s + " => ";
        // Show the start index '{' and the cursor '|'
        displayOutput(s, Data[i+3], log, index); 
    }

    delete t;
}
void TransliteratorAPITest::TestNullTransliterator(){
    UErrorCode status=U_ZERO_ERROR;
    UnicodeString s("Transliterate using null transliterator");
    Transliterator *nullTrans=Transliterator::createInstance("Any-Null", UTRANS_FORWARD, status);
    if (!assertSuccess(WHERE, status)) {
        return;
    }
    int32_t transLimit;
    int32_t start=0;
    int32_t limit=s.length();
    UnicodeString replaceable=s;
    transLimit=nullTrans->transliterate(replaceable, start, limit);
    if(transLimit != limit){
        errln("ERROR: NullTransliterator->transliterate() failed");
    }
    doTest((UnicodeString)"nulTrans->transliterate", replaceable, s);
    replaceable.remove();
    replaceable.append(s);
    UTransPosition index;
    index.contextStart =start;
    index.contextLimit = limit;
    index.start = 0;
    index.limit = limit;
    nullTrans->finishTransliteration(replaceable, index);
    if(index.start != limit){
        errln("ERROR: NullTransliterator->handleTransliterate() failed");
    }
    doTest((UnicodeString)"NullTransliterator->handleTransliterate", replaceable, s);
    callEverything(nullTrans, __LINE__);
    delete nullTrans;

    
}
void TransliteratorAPITest::TestRegisterUnregister(){
   
   UErrorCode status=U_ZERO_ERROR;
    /* Make sure it doesn't exist */
   if (Transliterator::createInstance("TestA-TestB", UTRANS_FORWARD, status) != nullptr) {
      errln("FAIL: TestA-TestB already registered\n");
      return;
   }
   /* Check inverse too 
   if (Transliterator::createInstance("TestA-TestB",
                                      (UTransDirection)UTRANS_REVERSE) != nullptr) {
      errln("FAIL: TestA-TestB inverse already registered\n");
      return;
   }
   */
   status =U_ZERO_ERROR;

   /* Create it */
   UParseError parseError;
   Transliterator *t = Transliterator::createFromRules("TestA-TestB",
                                                   "a<>b",
                                                   UTRANS_FORWARD, parseError,
                                                   status);
   /* Register it */
   Transliterator::registerInstance(t);

   /* Now check again -- should exist now*/
   Transliterator *s = Transliterator::createInstance("TestA-TestB", UTRANS_FORWARD, status);
   if (s == nullptr) {
      errln("FAIL: TestA-TestB not registered\n");
      return;
   }
   callEverything(s, __LINE__);
   callEverything(t, __LINE__);
   delete s;
   
   /* Check inverse too
   s = Transliterator::createInstance("TestA-TestB",
                                      (UTransDirection)UTRANS_REVERSE);
   if (s == nullptr) {
      errln("FAIL: TestA-TestB inverse not registered\n");
      return;
   }
   delete s;
   */
   
   /*unregister the instance*/
   Transliterator::unregister("TestA-TestB");
   /* now Make sure it doesn't exist */
   if (Transliterator::createInstance("TestA-TestB", UTRANS_FORWARD, status) != nullptr) {
      errln("FAIL: TestA-TestB isn't unregistered\n");
      return;
   }

}


int gTestFilter1ClassID = 0;
int gTestFilter2ClassID = 0;
int gTestFilter3ClassID = 0;

/**
 * Used by TestFiltering().
 */
class TestFilter1 : public UnicodeFilter {
    UClassID getDynamicClassID()const override { return &gTestFilter1ClassID; }
    virtual TestFilter1* clone() const override {
        return new TestFilter1(*this);
    }
    virtual UBool contains(UChar32 c) const override {
       if(c==0x63 || c==0x61 || c==0x43 || c==0x41)
          return false;
       else
          return true;
    }
    // Stubs
    virtual UnicodeString& toPattern(UnicodeString& result,
                                     UBool /*escapeUnprintable*/) const override {
        return result;
    }
    virtual UBool matchesIndexValue(uint8_t /*v*/) const override {
        return false;
    }
    virtual void addMatchSetTo(UnicodeSet& /*toUnionTo*/) const override {}
};
class TestFilter2 : public UnicodeFilter {
    UClassID getDynamicClassID() const override { return &gTestFilter2ClassID; }
    virtual TestFilter2* clone() const override {
        return new TestFilter2(*this);
    }
    virtual UBool contains(UChar32 c) const override {
        if(c==0x65 || c==0x6c)
           return false;
        else
           return true;
    }
    // Stubs
    virtual UnicodeString& toPattern(UnicodeString& result,
                                     UBool /*escapeUnprintable*/) const override {
        return result;
    }
    virtual UBool matchesIndexValue(uint8_t /*v*/) const override {
        return false;
    }
    virtual void addMatchSetTo(UnicodeSet& /*toUnionTo*/) const override {}
};
class TestFilter3 : public UnicodeFilter {
    UClassID getDynamicClassID() const override { return &gTestFilter3ClassID; }
    virtual TestFilter3* clone() const override {
        return new TestFilter3(*this);
    }
    virtual UBool contains(UChar32 c) const override {
        if(c==0x6f || c==0x77)
           return false;
        else
           return true;
    }
    // Stubs
    virtual UnicodeString& toPattern(UnicodeString& result,
                                     UBool /*escapeUnprintable*/) const override {
        return result;
    }
    virtual UBool matchesIndexValue(uint8_t /*v*/) const override {
        return false;
    }
    virtual void addMatchSetTo(UnicodeSet& /*toUnionTo*/) const override {}
};


void TransliteratorAPITest::TestGetAdoptFilter(){
    UErrorCode status = U_ZERO_ERROR;
    UParseError parseError;
    Transliterator *t=Transliterator::createInstance("Any-Hex", UTRANS_FORWARD, parseError, status);
    if(t == 0 || U_FAILURE(status)) {
        errln("Error creating transliterator %s", u_errorName(status));
        delete t;
        return;
    }
    const UnicodeFilter *u=t->getFilter();
    if(u != nullptr){
        errln("FAIL: getFilter failed. Didn't return null when the transliterator used no filtering");
        delete t;
        return;
    }

    UnicodeString got, temp, message;
    UnicodeString data="ABCabcbbCBa";
    temp = data;
    t->transliterate(temp);
    t->adoptFilter(new TestFilter1);

    got = data;
    t->transliterate(got);
    UnicodeString exp=UnicodeString("A\\u0042Ca\\u0062c\\u0062\\u0062C\\u0042a", "");
    message="transliteration after adoptFilter(a,A,c,C) ";
    doTest(message, got, exp);
         
    logln("Testing round trip");
    t->adoptFilter(const_cast<UnicodeFilter*>(u));
    if(t->getFilter() == nullptr)
       logln("OK: adoptFilter and getFilter round trip worked");
    else
       errln("FAIL: adoptFilter or getFilter round trip failed");  

    got = data;
    t->transliterate(got);
    exp=UnicodeString("\\u0041\\u0042\\u0043\\u0061\\u0062\\u0063\\u0062\\u0062\\u0043\\u0042\\u0061", "");
    message="transliteration after adopting null filter";
    doTest(message, got, exp);
    message="adoptFilter round trip"; 
    doTest("adoptFilter round trip", got, temp);

    t->adoptFilter(new TestFilter2);
    callEverything(t, __LINE__);
    data="heelloe";
    exp=UnicodeString("\\u0068eell\\u006Fe", "");
    got = data;
    t->transliterate(got);
    message="transliteration using (e,l) filter";
    doTest("transliteration using (e,l) filter", got, exp);

    data="are well";
    exp=UnicodeString("\\u0061\\u0072e\\u0020\\u0077ell", "");
    got = data;
    t->transliterate(got);
    doTest(message, got, exp);

    t->adoptFilter(new TestFilter3);
    data="ho, wow!";
    exp=UnicodeString("\\u0068o\\u002C\\u0020wow\\u0021", "");
    got = data;
    t->transliterate(got);
    message="transliteration using (o,w) filter";
    doTest("transliteration using (o,w) filter", got, exp);

    data="owl";
    exp=UnicodeString("ow\\u006C", "");
    got = data;
    t->transliterate(got);
    doTest("transliteration using (o,w) filter", got, exp);

    delete t;

}



void TransliteratorAPITest::keyboardAux(Transliterator *t, UnicodeString DATA[], UnicodeString& s, int32_t begin, int32_t end) {
    UTransPosition index={0, 0, 0, 0};
    UErrorCode status=U_ZERO_ERROR;
    for (int32_t i=begin; i<end; i=i+5) {
        UnicodeString log;
        if (DATA[i+0] != "") {
             log = s + " + " + DATA[i] + " -> ";
             index.contextStart=getInt(DATA[i+2]);
             index.contextLimit=index.limit=getInt(DATA[i+3]);
             index.start=getInt(DATA[i+4]);
             t->transliterate(s, index, DATA[i+0], status);
             if(U_FAILURE(status)){
                 errln("FAIL: " + t->getID()+ ".transliterate(Replaceable, int32_t[], UnicodeString, UErrorCode)-->" + (UnicodeString)u_errorName(status));
             continue;
             }
           log = s + " => ";
           t->finishTransliteration(s, index);
        }
         // Show the start index '{' and the cursor '|'
      displayOutput(s, DATA[i+1], log, index);
        
    }
}

void TransliteratorAPITest::displayOutput(const UnicodeString& got, const UnicodeString& expected, UnicodeString& log, UTransPosition& index){
 // Show the start index '{' and the cursor '|'
    UnicodeString a, b, c, d, e;
    got.extractBetween(0, index.contextStart, a);
    got.extractBetween(index.contextStart, index.start, b);
    got.extractBetween(index.start, index.limit, c);
    got.extractBetween(index.limit, index.contextLimit, d);
    got.extractBetween(index.contextLimit, got.length(), e);
    log.append(a).
        append((char16_t)0x7b/*{*/).
        append(b).
        append((char16_t)0x7c/*|*/).
        append(c).
        append((char16_t)0x7c).
        append(d).
        append((char16_t)0x7d/*}*/).
        append(e);
    if (got == expected) 
        logln("OK:" + prettify(log));
    else 
        errln("FAIL: " + prettify(log)  + ", expected " + prettify(expected));
}


/*Internal Functions used*/
void TransliteratorAPITest::doTest(const UnicodeString& message, const UnicodeString& result, const UnicodeString& expected){
    if (prettify(result) == prettify(expected)) 
        logln((UnicodeString)"Ok: " + prettify(message) + " passed \"" + prettify(expected) + "\"");
    else 
        dataerrln((UnicodeString)"FAIL:" + message + " failed  Got-->" + prettify(result)+ ", Expected--> " + prettify(expected) );
}


//
//  callEverything    call all of the const (non-destructive) methods on a
//                    transliterator, just to verify that they don't fail in some
//                    destructive way.
//
#define CEASSERT(a) UPRV_BLOCK_MACRO_BEGIN { \
    if (!(a)) { \
        errln("FAIL at line %d from line %d: %s", __LINE__, line, #a); \
        return; \
    } \
} UPRV_BLOCK_MACRO_END

void TransliteratorAPITest::callEverything(const Transliterator *tr, int line) {
    Transliterator *clonedTR = tr->clone();
    CEASSERT(clonedTR != nullptr);

    int32_t  maxcl = tr->getMaximumContextLength();
    CEASSERT(clonedTR->getMaximumContextLength() == maxcl);

    UnicodeString id;
    UnicodeString clonedId;
    id = tr->getID();
    clonedId = clonedTR->getID();
    CEASSERT(id == clonedId);

    const UnicodeFilter *filter = tr->getFilter();
    const UnicodeFilter *clonedFilter = clonedTR->getFilter();
    if (filter == nullptr || clonedFilter == nullptr) {
        // If one filter is nullptr they better both be nullptr.
        CEASSERT(filter == clonedFilter);
    } else {
        CEASSERT(filter != clonedFilter);
    }

    UnicodeString rules;
    UnicodeString clonedRules;
    rules = tr->toRules(rules, false);
    clonedRules = clonedTR->toRules(clonedRules, false);
    CEASSERT(rules == clonedRules);

    UnicodeSet sourceSet;
    UnicodeSet clonedSourceSet;
    tr->getSourceSet(sourceSet);
    clonedTR->getSourceSet(clonedSourceSet);
    CEASSERT(clonedSourceSet == sourceSet);

    UnicodeSet targetSet;
    UnicodeSet clonedTargetSet;
    tr->getTargetSet(targetSet);
    clonedTR->getTargetSet(clonedTargetSet);
    CEASSERT(targetSet == clonedTargetSet);

    UClassID classID = tr->getDynamicClassID();
    CEASSERT(classID == clonedTR->getDynamicClassID());
    CEASSERT(classID != 0);

    delete clonedTR;
}

static const int MyUnicodeFunctorTestClassID = 0;
class MyUnicodeFunctorTestClass : public UnicodeFunctor {
public:
    virtual UnicodeFunctor* clone() const override {return nullptr;}
    static UClassID getStaticClassID() {return (UClassID)&MyUnicodeFunctorTestClassID;}
    virtual UClassID getDynamicClassID() const override {return getStaticClassID();}
    virtual void setData(const TransliterationRuleData*) override {}
};

void TransliteratorAPITest::TestUnicodeFunctor() {
    MyUnicodeFunctorTestClass myClass;
    if (myClass.toMatcher() != nullptr) {
        errln("FAIL: UnicodeFunctor::toMatcher did not return nullptr");
    }
    if (myClass.toReplacer() != nullptr) {
        errln("FAIL: UnicodeFunctor::toReplacer did not return nullptr");
    }
}


#endif /* #if !UCONFIG_NO_TRANSLITERATION */
