// Copyright 2001, FreeHEP.
package org.freehep.graphicsio.font.truetype;

import java.io.IOException;

/**
 * CMAP Table.
 * 
 * @author Simon Fischer
 * @version $Id: freehep-graphicsio/src/main/java/org/freehep/graphicsio/font/truetype/TTFCMapTable.java 5641ca92a537 2005/11/26 00:15:35 duns $
 */
public class TTFCMapTable extends TTFTable {

    public class EncodingTable {
        public int platformID;

        public int encodingID;

        public long offset;

        public int format;

        public int length;

        public int version;

        public TableFormat tableFormat;

        public void readHeader() throws IOException {
            platformID = ttf.readUShort();
            encodingID = ttf.readUShort();
            offset = ttf.readULong();
        }

        public void readBody() throws IOException {
            ttf.seek(offset);
            format = ttf.readUShort();
            length = ttf.readUShort();
            version = ttf.readUShort();
            switch (format) {
            case 0:
                tableFormat = new TableFormat0();
                break;
            case 4:
                tableFormat = new TableFormat4();
                break;
            case 2:
            case 6:
                System.err.println("Unimplementet encoding table format: "
                        + format);
                break;
            default:
                System.err.println("Illegal value for encoding table format: "
                        + format);
                break;
            }
            if (tableFormat != null)
                tableFormat.read();
        }

        public String toString() {
            String str = "[encoding] PID:"
                    + platformID
                    + " EID:"
                    + encodingID
                    + " format:"
                    + format
                    + " v"
                    + version
                    + (tableFormat != null ? tableFormat.toString()
                            : " [no data read]");
            return str;
        }
    }

    public abstract class TableFormat {
        public abstract void read() throws IOException;

        public abstract int getGlyphIndex(int character);
    }

    public class TableFormat0 extends TableFormat {

        public int[] glyphIdArray = new int[256];

        public void read() throws IOException {
            for (int i = 0; i < glyphIdArray.length; i++)
                glyphIdArray[i] = ttf.readByte();
        }

        public String toString() {
            String str = "";
            for (int i = 0; i < glyphIdArray.length; i++) {
                if (i % 16 == 0)
                    str += "\n    " + Integer.toHexString(i / 16) + "x: ";
                String number = glyphIdArray[i] + "";
                while (number.length() < 3)
                    number = " " + number;
                str += number + " ";
            }
            return str;
        }

        public int getGlyphIndex(int character) {
            return glyphIdArray[character];
        }
    }

    public class TableFormat4 extends TableFormat {

        public int segCount;

        public int[] endCount, startCount, idRangeOffset;

        public short[] idDelta; // could be int (ushort) as well

        public void read() throws IOException {
            segCount = ttf.readUShort() / 2;
            // dump the next three ushorts to /dev/null as they guy
            // who invented them really must have drunk a lot
            ttf.readUShort();
            ttf.readUShort();
            ttf.readUShort();

            // endCount = readFFFFTerminatedUShortArray();
            endCount = ttf.readUShortArray(segCount);
            int reservedPad = ttf.readUShort();
            if (reservedPad != 0)
                System.err.println("reservedPad not 0, but " + reservedPad
                        + ".");

            startCount = ttf.readUShortArray(endCount.length);
            // the deltas should be unsigned, but due to
            // modulo arithmetic it makes no difference
            idDelta = ttf.readShortArray(endCount.length);
            idRangeOffset = ttf.readUShortArray(endCount.length);
        }

        public String toString() {
            String str = "\n   " + endCount.length + " sections:";
            for (int i = 0; i < endCount.length; i++)
                str += "\n    " + startCount[i] + " to " + endCount[i] + " : "
                        + idDelta[i] + " (" + idRangeOffset[i] + ")";
            return str;
        }

        public int getGlyphIndex(int character) {
            return 0;
        }
    }

    public int version;

    public EncodingTable encodingTable[];

    public String getTag() {
        return "cmap";
    }

    public void readTable() throws IOException {
        version = ttf.readUShort();
        encodingTable = new EncodingTable[ttf.readUShort()];
        for (int i = 0; i < encodingTable.length; i++) {
            encodingTable[i] = new EncodingTable();
            encodingTable[i].readHeader();
        }
        for (int i = 0; i < encodingTable.length; i++) {
            encodingTable[i].readBody();
        }
    }

    public String toString() {
        String str = super.toString() + " v" + version;
        for (int i = 0; i < encodingTable.length; i++)
            str += "\n  " + encodingTable[i];
        return str;
    }
}
