using System;
using System.Text;
/*
 * This file is part of the iText project.
 * Copyright (c) 1998-2012 1T3XT BVBA
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * you must retain the producer line in every PDF that is created or manipulated
 * using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: sales@itextpdf.com
 */

namespace iTextSharp.text.pdf {

    /**
    * Shape arabic characters. This code was inspired by an LGPL'ed C library:
    * Pango ( see http://www.pango.com/ ). Note that the code of this is the
    * original work of Paulo Soares. Hence it is perfectly justifiable to distribute
    * it under the MPL.
    *
    * @author Paulo Soares
    */
    public class ArabicLigaturizer {
        
        static bool IsVowel(char s) {
            return ((s >= '\u064B') && (s <= '\u0655')) || (s == '\u0670');
        }

        static char Charshape(char s, int which)
        /* which 0=isolated 1=final 2=initial 3=medial */
        {
            int l, r, m;
            if ((s >= '\u0621') && (s <= '\u06D3')) {
                l = 0;
                r = chartable.Length - 1;
                while (l <= r) {
                    m = (l + r) / 2;
                    if (s == chartable[m][0]) {
                        return chartable[m][which + 1];
                    }
                    else if (s < chartable[m][0]) {
                        r = m - 1;
                    }
                    else {
                        l = m + 1;
                    }
                }
            }
            else if (s >= '\ufef5' && s <= '\ufefb')
                return (char)(s + which);
            return s;
        }

        static int Shapecount(char s) {
            int l, r, m;
            if ((s >= '\u0621') && (s <= '\u06D3') && !IsVowel(s)) {
                l = 0;
                r = chartable.Length - 1;
                while (l <= r) {
                    m = (l + r) / 2;
                    if (s == chartable[m][0]) {
                        return chartable[m].Length - 1;
                    }
                    else if (s < chartable[m][0]) {
                        r = m - 1;
                    }
                    else {
                        l = m + 1;
                    }
                }
            }
            else if (s == ZWJ) {
                return 4;
            }
            return 1;
        }
        
        static int Ligature(char newchar, Charstruct oldchar) {
        /* 0 == no ligature possible; 1 == vowel; 2 == two chars; 3 == Lam+Alef */
            int retval = 0;
            
            if (oldchar.basechar == 0)
                return 0;
            if (IsVowel(newchar)) {
                retval = 1;
                if ((oldchar.vowel != 0) && (newchar != SHADDA)) {
                    retval = 2;           /* we eliminate the old vowel .. */
                }
                switch (newchar) {
                    case SHADDA:
                        if (oldchar.mark1 == 0) {
                            oldchar.mark1 = SHADDA;
                        }
                        else {
                            return 0;         /* no ligature possible */
                        }
                        break;
                    case HAMZABELOW:
                        switch (oldchar.basechar) {
                            case ALEF:
                                oldchar.basechar = ALEFHAMZABELOW;
                                retval = 2;
                                break;
                            case LAM_ALEF:
                                oldchar.basechar = LAM_ALEFHAMZABELOW;
                                retval = 2;
                                break;
                            default:
                                oldchar.mark1 = HAMZABELOW;
                                break;
                        }
                        break;
                    case HAMZAABOVE:
                        switch (oldchar.basechar) {
                            case ALEF:
                                oldchar.basechar = ALEFHAMZA;
                                retval = 2;
                                break;
                            case LAM_ALEF:
                                oldchar.basechar = LAM_ALEFHAMZA;
                                retval = 2;
                                break;
                            case WAW:
                                oldchar.basechar = WAWHAMZA;
                                retval = 2;
                                break;
                            case YEH:
                            case ALEFMAKSURA:
                            case FARSIYEH:
                                oldchar.basechar = YEHHAMZA;
                                retval = 2;
                                break;
                            default:           /* whatever sense this may make .. */
                                oldchar.mark1 = HAMZAABOVE;
                                break;
                        }
                        break;
                    case MADDA:
                        switch (oldchar.basechar) {
                            case ALEF:
                                oldchar.basechar = ALEFMADDA;
                                retval = 2;
                                break;
                        }
                        break;
                    default:
                        oldchar.vowel = newchar;
                        break;
                }
                if (retval == 1) {
                    oldchar.lignum++;
                }
                return retval;
            }
            if (oldchar.vowel != 0) {  /* if we already joined a vowel, we can't join a Hamza */
                return 0;
            }
            
            switch (oldchar.basechar) {
                case LAM:
                    switch (newchar) {
                        case ALEF:
                            oldchar.basechar = LAM_ALEF;
                            oldchar.numshapes = 2;
                            retval = 3;
                            break;
                        case ALEFHAMZA:
                            oldchar.basechar = LAM_ALEFHAMZA;
                            oldchar.numshapes = 2;
                            retval = 3;
                            break;
                        case ALEFHAMZABELOW:
                            oldchar.basechar = LAM_ALEFHAMZABELOW;
                            oldchar.numshapes = 2;
                            retval = 3;
                            break;
                        case ALEFMADDA:
                            oldchar.basechar = LAM_ALEFMADDA;
                            oldchar.numshapes = 2;
                            retval = 3;
                            break;
                    }
                    break;
                case (char)0:
                    oldchar.basechar = newchar;
                    oldchar.numshapes = Shapecount(newchar);
                    retval = 1;
                    break;
            }
            return retval;
        }
        
        static void Copycstostring(StringBuilder str, Charstruct s, int level) {
        /* s is a shaped charstruct; i is the index into the string */
            if (s.basechar == 0)
                return;
            
            str.Append(s.basechar);
            s.lignum--;
            if (s.mark1 != 0) {
                if ((level & ar_novowel) == 0) {
                    str.Append(s.mark1);
                    s.lignum--;
                }
                else {
                    s.lignum--;
                }
            }
            if (s.vowel != 0) {
                if ((level & ar_novowel) == 0) {
                    str.Append(s.vowel);
                    s.lignum--;
                }
                else {                       /* vowel elimination */
                    s.lignum--;
                }
            }
        }

        // return len
        internal static void Doublelig(StringBuilder str, int level)
        /* Ok. We have presentation ligatures in our font. */
        {
            int len;
            int olen = len = str.Length;
            int j = 0, si = 1;
            char lapresult;
            
            while (si < olen) {
                lapresult = (char)0;
                if ((level & ar_composedtashkeel) != 0) {
                    switch (str[j]) {
                        case SHADDA:
                            switch (str[si]) {
                                case KASRA:
                                    lapresult = '\uFC62';
                                    break;
                                case FATHA:
                                    lapresult = '\uFC60';
                                    break;
                                case DAMMA:
                                    lapresult = '\uFC61';
                                    break;
                                case '\u064C':
                                    lapresult = '\uFC5E';
                                    break;
                                case '\u064D':
                                    lapresult = '\uFC5F';
                                    break;
                            }
                            break;
                        case KASRA:
                            if (str[si] == SHADDA)
                                lapresult = '\uFC62';
                            break;
                        case FATHA:
                            if (str[si] == SHADDA)
                                lapresult = '\uFC60';
                            break;
                        case DAMMA:
                            if (str[si] == SHADDA)
                                lapresult = '\uFC61';
                            break;
                    }
                }
                
                if ((level & ar_lig) != 0) {
                    switch (str[j]) {
                        case '\uFEDF':       /* LAM initial */
                            switch (str[si]) {
                                case '\uFE9E':
                                    lapresult = '\uFC3F';
                                    break;        /* JEEM final */
                                case '\uFEA0':
                                    lapresult = '\uFCC9';
                                    break;        /* JEEM medial */
                                case '\uFEA2':
                                    lapresult = '\uFC40';
                                    break;        /* HAH final */
                                case '\uFEA4':
                                    lapresult = '\uFCCA';
                                    break;        /* HAH medial */
                                case '\uFEA6':
                                    lapresult = '\uFC41';
                                    break;        /* KHAH final */
                                case '\uFEA8':
                                    lapresult = '\uFCCB';
                                    break;        /* KHAH medial */
                                case '\uFEE2':
                                    lapresult = '\uFC42';
                                    break;        /* MEEM final */
                                case '\uFEE4':
                                    lapresult = '\uFCCC';
                                    break;        /* MEEM medial */
                            }
                            break;
                        case '\uFE97':       /* TEH inital */
                            switch (str[si]) {
                                case '\uFEA0':
                                    lapresult = '\uFCA1';
                                    break;        /* JEEM medial */
                                case '\uFEA4':
                                    lapresult = '\uFCA2';
                                    break;        /* HAH medial */
                                case '\uFEA8':
                                    lapresult = '\uFCA3';
                                    break;        /* KHAH medial */
                            }
                            break;
                        case '\uFE91':       /* BEH inital */
                            switch (str[si]) {
                                case '\uFEA0':
                                    lapresult = '\uFC9C';
                                    break;        /* JEEM medial */
                                case '\uFEA4':
                                    lapresult = '\uFC9D';
                                    break;        /* HAH medial */
                                case '\uFEA8':
                                    lapresult = '\uFC9E';
                                    break;        /* KHAH medial */
                            }
                            break;
                        case '\uFEE7':       /* NOON inital */
                            switch (str[si]) {
                                case '\uFEA0':
                                    lapresult = '\uFCD2';
                                    break;        /* JEEM initial */
                                case '\uFEA4':
                                    lapresult = '\uFCD3';
                                    break;        /* HAH medial */
                                case '\uFEA8':
                                    lapresult = '\uFCD4';
                                    break;        /* KHAH medial */
                            }
                            break;
                            
                        case '\uFEE8':       /* NOON medial */
                            switch (str[si]) {
                                case '\uFEAE':
                                    lapresult = '\uFC8A';
                                    break;        /* REH final  */
                                case '\uFEB0':
                                    lapresult = '\uFC8B';
                                    break;        /* ZAIN final */
                            }
                            break;
                        case '\uFEE3':       /* MEEM initial */
                            switch (str[si]) {
                                case '\uFEA0':
                                    lapresult = '\uFCCE';
                                    break;        /* JEEM medial */
                                case '\uFEA4':
                                    lapresult = '\uFCCF';
                                    break;        /* HAH medial */
                                case '\uFEA8':
                                    lapresult = '\uFCD0';
                                    break;        /* KHAH medial */
                                case '\uFEE4':
                                    lapresult = '\uFCD1';
                                    break;        /* MEEM medial */
                            }
                            break;
                            
                        case '\uFED3':       /* FEH initial */
                            switch (str[si]) {
                                case '\uFEF2':
                                    lapresult = '\uFC32';
                                    break;        /* YEH final */
                            }
                            break;
                            
                        default:
                            break;
                    }                   /* end switch string[si] */
                }
                if (lapresult != 0) {
                    str[j] = lapresult;
                    len--;
                    si++;                 /* jump over one character */
                    /* we'll have to change this, too. */
                }
                else {
                    j++;
                    str[j] = str[si];
                    si++;
                }
            }
            str.Length = len;
        }

        static bool Connects_to_left(Charstruct a) {
            return a.numshapes > 2;
        }
        
        internal static void Shape(char[] text, StringBuilder str, int level) {
    /* string is assumed to be empty and big enough.
    * text is the original text.
    * This routine does the basic arabic reshaping.
    * *len the number of non-null characters.
    *
    * Note: We have to unshape each character first!
    */
            int join;
            int which;
            char nextletter;
            
            int p = 0;                     /* initialize for output */
            Charstruct oldchar = new Charstruct();
            Charstruct curchar = new Charstruct();
            while (p < text.Length) {
                nextletter = text[p++];
                //nextletter = unshape (nextletter);
                
                join = Ligature(nextletter, curchar);
                if (join == 0) {                       /* shape curchar */
                    int nc = Shapecount(nextletter);
                    //(*len)++;
                    if (nc == 1) {
                        which = 0;        /* final or isolated */
                    }
                    else {
                        which = 2;        /* medial or initial */
                    }
                    if (Connects_to_left(oldchar)) {
                        which++;
                    }
                    
                    which = which % (curchar.numshapes);
                    curchar.basechar = Charshape(curchar.basechar, which);
                    
                    /* get rid of oldchar */
                    Copycstostring(str, oldchar, level);
                    oldchar = curchar;    /* new values in oldchar */
                    
                    /* init new curchar */
                    curchar = new Charstruct();
                    curchar.basechar = nextletter;
                    curchar.numshapes = nc;
                    curchar.lignum++;
                    //          (*len) += unligature (&curchar, level);
                }
                else if (join == 1) {
                }
                //      else
                //        {
                //          (*len) += unligature (&curchar, level);
                //        }
                //      p = g_utf8_next_char (p);
            }
            
            /* Handle last char */
            if (Connects_to_left(oldchar))
                which = 1;
            else
                which = 0;
            which = which % (curchar.numshapes);
            curchar.basechar = Charshape(curchar.basechar, which);
            
            /* get rid of oldchar */
            Copycstostring(str, oldchar, level);
            Copycstostring(str, curchar, level);
        }

        internal static int Arabic_shape(char[] src, int srcoffset, int srclength, char[] dest, int destoffset, int destlength, int level) {
            char[] str = new char[srclength];
            for (int k = srclength + srcoffset - 1; k >= srcoffset; --k)
                str[k - srcoffset] = src[k];
            StringBuilder str2 = new StringBuilder(srclength);
            Shape(str, str2, level);
            if ((level & (ar_composedtashkeel | ar_lig)) != 0)
                Doublelig(str2, level);
    //        string.Reverse();
            System.Array.Copy(str2.ToString().ToCharArray(), 0, dest, destoffset, str2.Length);
            return str2.Length;
        }

        internal static void ProcessNumbers(char[] text, int offset, int length, int options) {
            int limit = offset + length;
            if ((options & DIGITS_MASK) != 0) {
                char digitBase = '\u0030'; // European digits
                switch (options & DIGIT_TYPE_MASK) {
                    case DIGIT_TYPE_AN:
                        digitBase = '\u0660';  // Arabic-Indic digits
                        break;
                        
                    case DIGIT_TYPE_AN_EXTENDED:
                        digitBase = '\u06f0';  // Eastern Arabic-Indic digits (Persian and Urdu)
                        break;
                        
                    default:
                        break;
                }
                
                switch (options & DIGITS_MASK) {
                    case DIGITS_EN2AN: {
                        int digitDelta = digitBase - '\u0030';
                        for (int i = offset; i < limit; ++i) {
                            char ch = text[i];
                            if (ch <= '\u0039' && ch >= '\u0030') {
                                text[i] += (char)digitDelta;
                            }
                        }
                    }
                    break;
                    
                    case DIGITS_AN2EN: {
                        char digitTop = (char)(digitBase + 9);
                        int digitDelta = '\u0030' - digitBase;
                        for (int i = offset; i < limit; ++i) {
                            char ch = text[i];
                            if (ch <= digitTop && ch >= digitBase) {
                                text[i] += (char)digitDelta;
                            }
                        }
                    }
                    break;
                    
                    case DIGITS_EN2AN_INIT_LR:
                        ShapeToArabicDigitsWithContext(text, 0, length, digitBase, false);
                        break;
                        
                    case DIGITS_EN2AN_INIT_AL:
                        ShapeToArabicDigitsWithContext(text, 0, length, digitBase, true);
                        break;
                        
                    default:
                        break;
                }
            }
        }
        
        internal static void ShapeToArabicDigitsWithContext(char[] dest, int start, int length, char digitBase,  bool lastStrongWasAL) {
            digitBase -= '0'; // move common adjustment out of loop
     
            int limit = start + length;
            for (int i = start; i < limit; ++i) {
                char ch = dest[i];
                switch (BidiOrder.GetDirection(ch)) {
                case BidiOrder.L:
                case BidiOrder.R:
                    lastStrongWasAL = false;
                    break;
                case BidiOrder.AL:
                    lastStrongWasAL = true;
                    break;
                case BidiOrder.EN:
                    if (lastStrongWasAL && ch <= '\u0039') {
                        dest[i] = (char)(ch + digitBase);
                    }
                    break;
                default:
                    break;
                }
            }
        }

        private const char ALEF = '\u0627';
        private const char ALEFHAMZA = '\u0623';
        private const char ALEFHAMZABELOW = '\u0625';
        private const char ALEFMADDA = '\u0622';
        private const char LAM = '\u0644';
        private const char HAMZA = '\u0621';
        private const char TATWEEL = '\u0640';
        private const char ZWJ = '\u200D';

        private const char HAMZAABOVE = '\u0654';
        private const char HAMZABELOW = '\u0655';

        private const char WAWHAMZA = '\u0624';
        private const char YEHHAMZA = '\u0626';
        private const char WAW = '\u0648';
        private const char ALEFMAKSURA = '\u0649';
        private const char YEH = '\u064A';
        private const char FARSIYEH = '\u06CC';

        private const char SHADDA = '\u0651';
        private const char KASRA = '\u0650';
        private const char FATHA = '\u064E';
        private const char DAMMA = '\u064F';
        private const char MADDA = '\u0653';

        private const char LAM_ALEF = '\uFEFB';
        private const char LAM_ALEFHAMZA = '\uFEF7';
        private const char LAM_ALEFHAMZABELOW = '\uFEF9';
        private const char LAM_ALEFMADDA = '\uFEF5';

        private static char[][] chartable = {
            new char[]{'\u0621', '\uFE80'}, /* HAMZA */
            new char[]{'\u0622', '\uFE81', '\uFE82'}, /* ALEF WITH MADDA ABOVE */
            new char[]{'\u0623', '\uFE83', '\uFE84'}, /* ALEF WITH HAMZA ABOVE */
            new char[]{'\u0624', '\uFE85', '\uFE86'}, /* WAW WITH HAMZA ABOVE */
            new char[]{'\u0625', '\uFE87', '\uFE88'}, /* ALEF WITH HAMZA BELOW */
            new char[]{'\u0626', '\uFE89', '\uFE8A', '\uFE8B', '\uFE8C'}, /* YEH WITH HAMZA ABOVE */
            new char[]{'\u0627', '\uFE8D', '\uFE8E'}, /* ALEF */
            new char[]{'\u0628', '\uFE8F', '\uFE90', '\uFE91', '\uFE92'}, /* BEH */
            new char[]{'\u0629', '\uFE93', '\uFE94'}, /* TEH MARBUTA */
            new char[]{'\u062A', '\uFE95', '\uFE96', '\uFE97', '\uFE98'}, /* TEH */
            new char[]{'\u062B', '\uFE99', '\uFE9A', '\uFE9B', '\uFE9C'}, /* THEH */
            new char[]{'\u062C', '\uFE9D', '\uFE9E', '\uFE9F', '\uFEA0'}, /* JEEM */
            new char[]{'\u062D', '\uFEA1', '\uFEA2', '\uFEA3', '\uFEA4'}, /* HAH */
            new char[]{'\u062E', '\uFEA5', '\uFEA6', '\uFEA7', '\uFEA8'}, /* KHAH */
            new char[]{'\u062F', '\uFEA9', '\uFEAA'}, /* DAL */
            new char[]{'\u0630', '\uFEAB', '\uFEAC'}, /* THAL */
            new char[]{'\u0631', '\uFEAD', '\uFEAE'}, /* REH */
            new char[]{'\u0632', '\uFEAF', '\uFEB0'}, /* ZAIN */
            new char[]{'\u0633', '\uFEB1', '\uFEB2', '\uFEB3', '\uFEB4'}, /* SEEN */
            new char[]{'\u0634', '\uFEB5', '\uFEB6', '\uFEB7', '\uFEB8'}, /* SHEEN */
            new char[]{'\u0635', '\uFEB9', '\uFEBA', '\uFEBB', '\uFEBC'}, /* SAD */
            new char[]{'\u0636', '\uFEBD', '\uFEBE', '\uFEBF', '\uFEC0'}, /* DAD */
            new char[]{'\u0637', '\uFEC1', '\uFEC2', '\uFEC3', '\uFEC4'}, /* TAH */
            new char[]{'\u0638', '\uFEC5', '\uFEC6', '\uFEC7', '\uFEC8'}, /* ZAH */
            new char[]{'\u0639', '\uFEC9', '\uFECA', '\uFECB', '\uFECC'}, /* AIN */
            new char[]{'\u063A', '\uFECD', '\uFECE', '\uFECF', '\uFED0'}, /* GHAIN */
            new char[]{'\u0640', '\u0640', '\u0640', '\u0640', '\u0640'}, /* TATWEEL */
            new char[]{'\u0641', '\uFED1', '\uFED2', '\uFED3', '\uFED4'}, /* FEH */
            new char[]{'\u0642', '\uFED5', '\uFED6', '\uFED7', '\uFED8'}, /* QAF */
            new char[]{'\u0643', '\uFED9', '\uFEDA', '\uFEDB', '\uFEDC'}, /* KAF */
            new char[]{'\u0644', '\uFEDD', '\uFEDE', '\uFEDF', '\uFEE0'}, /* LAM */
            new char[]{'\u0645', '\uFEE1', '\uFEE2', '\uFEE3', '\uFEE4'}, /* MEEM */
            new char[]{'\u0646', '\uFEE5', '\uFEE6', '\uFEE7', '\uFEE8'}, /* NOON */
            new char[]{'\u0647', '\uFEE9', '\uFEEA', '\uFEEB', '\uFEEC'}, /* HEH */
            new char[]{'\u0648', '\uFEED', '\uFEEE'}, /* WAW */
            new char[]{'\u0649', '\uFEEF', '\uFEF0', '\uFBE8', '\uFBE9'}, /* ALEF MAKSURA */
            new char[]{'\u064A', '\uFEF1', '\uFEF2', '\uFEF3', '\uFEF4'}, /* YEH */
            new char[]{'\u0671', '\uFB50', '\uFB51'}, /* ALEF WASLA */
            new char[]{'\u0679', '\uFB66', '\uFB67', '\uFB68', '\uFB69'}, /* TTEH */
            new char[]{'\u067A', '\uFB5E', '\uFB5F', '\uFB60', '\uFB61'}, /* TTEHEH */
            new char[]{'\u067B', '\uFB52', '\uFB53', '\uFB54', '\uFB55'}, /* BEEH */
            new char[]{'\u067E', '\uFB56', '\uFB57', '\uFB58', '\uFB59'}, /* PEH */
            new char[]{'\u067F', '\uFB62', '\uFB63', '\uFB64', '\uFB65'}, /* TEHEH */
            new char[]{'\u0680', '\uFB5A', '\uFB5B', '\uFB5C', '\uFB5D'}, /* BEHEH */
            new char[]{'\u0683', '\uFB76', '\uFB77', '\uFB78', '\uFB79'}, /* NYEH */
            new char[]{'\u0684', '\uFB72', '\uFB73', '\uFB74', '\uFB75'}, /* DYEH */
            new char[]{'\u0686', '\uFB7A', '\uFB7B', '\uFB7C', '\uFB7D'}, /* TCHEH */
            new char[]{'\u0687', '\uFB7E', '\uFB7F', '\uFB80', '\uFB81'}, /* TCHEHEH */
            new char[]{'\u0688', '\uFB88', '\uFB89'}, /* DDAL */
            new char[]{'\u068C', '\uFB84', '\uFB85'}, /* DAHAL */
            new char[]{'\u068D', '\uFB82', '\uFB83'}, /* DDAHAL */
            new char[]{'\u068E', '\uFB86', '\uFB87'}, /* DUL */
            new char[]{'\u0691', '\uFB8C', '\uFB8D'}, /* RREH */
            new char[]{'\u0698', '\uFB8A', '\uFB8B'}, /* JEH */
            new char[]{'\u06A4', '\uFB6A', '\uFB6B', '\uFB6C', '\uFB6D'}, /* VEH */
            new char[]{'\u06A6', '\uFB6E', '\uFB6F', '\uFB70', '\uFB71'}, /* PEHEH */
            new char[]{'\u06A9', '\uFB8E', '\uFB8F', '\uFB90', '\uFB91'}, /* KEHEH */
            new char[]{'\u06AD', '\uFBD3', '\uFBD4', '\uFBD5', '\uFBD6'}, /* NG */
            new char[]{'\u06AF', '\uFB92', '\uFB93', '\uFB94', '\uFB95'}, /* GAF */
            new char[]{'\u06B1', '\uFB9A', '\uFB9B', '\uFB9C', '\uFB9D'}, /* NGOEH */
            new char[]{'\u06B3', '\uFB96', '\uFB97', '\uFB98', '\uFB99'}, /* GUEH */
            new char[]{'\u06BA', '\uFB9E', '\uFB9F'}, /* NOON GHUNNA */
            new char[]{'\u06BB', '\uFBA0', '\uFBA1', '\uFBA2', '\uFBA3'}, /* RNOON */
            new char[]{'\u06BE', '\uFBAA', '\uFBAB', '\uFBAC', '\uFBAD'}, /* HEH DOACHASHMEE */
            new char[]{'\u06C0', '\uFBA4', '\uFBA5'}, /* HEH WITH YEH ABOVE */
            new char[]{'\u06C1', '\uFBA6', '\uFBA7', '\uFBA8', '\uFBA9'}, /* HEH GOAL */
            new char[]{'\u06C5', '\uFBE0', '\uFBE1'}, /* KIRGHIZ OE */
            new char[]{'\u06C6', '\uFBD9', '\uFBDA'}, /* OE */
            new char[]{'\u06C7', '\uFBD7', '\uFBD8'}, /* U */
            new char[]{'\u06C8', '\uFBDB', '\uFBDC'}, /* YU */
            new char[]{'\u06C9', '\uFBE2', '\uFBE3'}, /* KIRGHIZ YU */
            new char[]{'\u06CB', '\uFBDE', '\uFBDF'}, /* VE */
            new char[]{'\u06CC', '\uFBFC', '\uFBFD', '\uFBFE', '\uFBFF'}, /* FARSI YEH */
            new char[]{'\u06D0', '\uFBE4', '\uFBE5', '\uFBE6', '\uFBE7'}, /* E */
            new char[]{'\u06D2', '\uFBAE', '\uFBAF'}, /* YEH BARREE */
            new char[]{'\u06D3', '\uFBB0', '\uFBB1'} /* YEH BARREE WITH HAMZA ABOVE */
            };

            public const int ar_nothing  = 0x0;
            public const int ar_novowel = 0x1;
            public const int ar_composedtashkeel = 0x4;
            public const int ar_lig = 0x8;
            /**
            * Digit shaping option: Replace European digits (U+0030...U+0039) by Arabic-Indic digits.
            */
            public const int DIGITS_EN2AN = 0x20;
            
            /**
            * Digit shaping option: Replace Arabic-Indic digits by European digits (U+0030...U+0039).
            */
            public const int DIGITS_AN2EN = 0x40;
            
            /**
            * Digit shaping option:
            * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
            * if the most recent strongly directional character
            * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
            * The initial state at the start of the text is assumed to be not an Arabic,
            * letter, so European digits at the start of the text will not change.
            * Compare to DIGITS_ALEN2AN_INIT_AL.
            */
            public const int DIGITS_EN2AN_INIT_LR = 0x60;
            
            /**
            * Digit shaping option:
            * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
            * if the most recent strongly directional character
            * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
            * The initial state at the start of the text is assumed to be an Arabic,
            * letter, so European digits at the start of the text will change.
            * Compare to DIGITS_ALEN2AN_INT_LR.
            */
            public const int DIGITS_EN2AN_INIT_AL = 0x80;
            
            /** Not a valid option value. */
            private const int DIGITS_RESERVED = 0xa0;
            
            /**
            * Bit mask for digit shaping options.
            */
            public const int DIGITS_MASK = 0xe0;
            
            /**
            * Digit type option: Use Arabic-Indic digits (U+0660...U+0669).
            */
            public const int DIGIT_TYPE_AN = 0;
            
            /**
            * Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9).
            */
            public const int DIGIT_TYPE_AN_EXTENDED = 0x100;

            /**
            * Bit mask for digit type options.
            */
            public const int DIGIT_TYPE_MASK = '\u0100'; // '\u3f00'?

            private class Charstruct {
                internal char basechar;
                internal char mark1;               /* has to be initialized to zero */
                internal char vowel;
                internal int lignum;           /* is a ligature with lignum aditional characters */
                internal int numshapes = 1;
            };


    }
}
