/*
 *
 */

import java.io.*;

public class UnicodeData {

    private String   UCDname;
    private int      uOffset    = 0;
    private int      uPrevChar  = 0;
    static  byte []  UniTable;
    private int  []  uIndex;

    public UnicodeData(String fname) {
        UCDname = fname;
        readUnicodeTable();
    }


    public UnicodeData() {
        this("UnicodeData.txt");
    }

    /*
     * read in the Unicode Table, which must be in the lib directory or
     * in the current directory.
     */
    private int  readUnicodeTable() {
        FileInputStream iFile;
        UniTable = new byte[1];
        UniTable[0] = (byte)1;
        uIndex = new int[256];
        boolean start;

        String fname = System.getProperty("java.home") + File.separator +
                       "lib" + File.separator;

        File f = new File(UCDname);
        if (!f.canRead()) {
            f = new File(fname + "UnicodeData.txt");
            if (!f.canRead()) {
                return 1;
            }
        }

        int len = (int)f.length();
        UniTable = new byte [len+2];
        UniTable[0] = (byte)1;

        try {
            iFile = new FileInputStream(f);
        } catch (Exception e) {
            return 1;
        }

        try {
            len = iFile.read(UniTable, 0, len);
        } catch (Exception e) {
            UniTable[0] = 1;
            return 1;
        }

        UniTable[len]   = 0;
        UniTable[len+1] = 1;

        start = true;
        for (int i=0; i<len; i++) {

            if (UniTable[i] == (byte)'\r' || UniTable[i] == (byte)'\n') {
                UniTable[i] = 0;
                start = true;
            } else {
                if (start) {
                    int val = getUnicodeValue(i);
                    if (val < 0x10000 && (val & 0xff) == 0) {
                        uIndex[val>>8] = i;
                    }
                    start = false;
                }
            }
        }
        return 0;
    }


    /*
     * Get a hex value from an offset in the unicode table
     */
    private int  getUnicodeValue(int offset) {
        int    val;
        int    gotone;
        char   ch;

        val = 0;
        gotone = 0;
        for(;;) {
            ch = (char)(UniTable[offset++]&0xff);
            if (ch>='0' && ch<='9') {
                ch -= '0';
            } else {
                if (ch>='a' && ch<='f') {
                    ch -= 'a'-10;
                } else {
                    if (ch>='A' && ch<='F') {
                        ch -= 'A'-10;
                    } else {
                        if (ch == ';')
                            break;
                        return 0x1000000;
                    }
                }
            }
            val *= 16;
            val += ch;
            gotone = 1;
        }
        if (gotone == 0)
            return 0x1000000;
        return val;
    }


    public String knownRange(int uc) {
        if (uc < 0x3400)
            return "";
        if (uc <= 0x4db5)
            return "3400;<CJK Extension A>;Lo;0;L;;;;;N;;;;;";
        if (uc < 0x4e00)
            return "";
        if (uc<=0x9fa5)
            return "4E00;<CJK Ideograph>;Lo;0;L;;;;;N;;;;;";
        if (uc < 0xac00)
            return "";
        if (uc<=0xd7a3)
            return "AC00;<Hangul Syllable>;Lo;0;L;;;;;N;;;;;";
        if (uc < 0xd800)
            return "";
        if (uc <= 0xdbff)
            return "DB80;<High Surrogate>;Cs;0;L;;;;;N;;;;;";
        if (uc < 0xdc00)
            return "";
        if (uc <= 0xdfff)
            return "DC00;<Low Surrogate>;Cs;0;L;;;;;N;;;;;";
        if (uc < 0xe000)
            return "";
        if (uc <= 0xf8ff)
            return "E000;<User defined character>;Co;0;L;;;;;N;;;;;";
        if (uc < 0x20000)
            return "";
        if (uc <= 0x2a6d6)
            return "20000;<CJK Extension B>;Lo;0;L;;;;;N;;;;;";
        if (uc < 0xf0000)
            return "";
        if (uc <= 0xffffd)
            return "F0000;<Plane 15 Private Use>;Co;0;L;;;;;N;;;;;";
        if (uc < 0x100000)
            return "";
        if (uc <= 0x10fffd)
            return "100000;<Plane 16 Private Use>;Co;0;L;;;;;N;;;;;";
        return "";
    }


    /*
     * Get the entire unicode info string for a character
     */
    public String getUnicodeInfo(int uc) {
        if (!"".equals(knownRange(uc))) {
            return knownRange(uc);
        }
        if (UniTable.length==1)
            return "0000;;Xx;;;;;;;;;;;;";

        int offset = uIndex[uc>=0xff00 ? 255 : (uc>>8)];
        if (uc >= uPrevChar && (uc>>8 == uPrevChar>>8))
            offset = uOffset;

        while (UniTable[offset] != 1) {
            int  i = getUnicodeValue(offset);
            int  thisuc = i;
            if (thisuc > uc && i != 0x1000000)
                break;
            int  endoffset = offset+1;
            while (UniTable[endoffset] != 0)
                endoffset++;

            if (thisuc == uc && i != 0x1000000) {
                uOffset = offset;
                uPrevChar = uc;
                return new String(UniTable, offset, endoffset-offset);
            }

            offset = endoffset;
            while (UniTable[offset] == 0)
                offset++;
        }
        return "";
    }


    /*
     * Get a field of the unicode info string for a character
     */
    public String getUnicodeInfo(int ch, int token) {
        return UnicodeData.getUnicodeInfo(getUnicodeInfo(ch), token);
    }


    /*
     * Get a single field from the unicode string
     */
    public static String getUnicodeInfo(String s, int token) {
        int  offset = 0;
        int  endoffset = 0;

        for (int i=0; i<token; i++) {
            try {
                offset = s.indexOf(';', offset+1);
            } catch(Exception ex) {
                offset = -1;
            }
            if (offset<0)
                return "";
        }
        endoffset = s.indexOf(';', offset+1);
        if (endoffset>offset) {
            return s.substring(offset+1, endoffset);
        }
        return "";
    }
}
