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

import java.io.IOException;

/**
 * CMAP Table.
 *
 *  @author Simon Fischer
 *  @version $Id: TTFCMapTable.java,v 1.1 2001/11/29 01:45:29 duns Exp $
 */
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;
    }
}
