import java.lang.*;
import java.io.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.image.*;
import java.awt.geom.*;
import Acme.JPM.Encoders.*;
import com.sun.image.codec.jpeg.*;

class IDSCompose {
    //AffineTransform scaler;
    //AffineTransformOp scaleOp;
    BufferedImage dst, tmpsrc;
    Image tmpdst;
    Graphics2D dstgraphics;
    Canvas dummycanvas;
    IDTree root;
    int charsize, idsIndex;
    GifEncoder gifwriter;
    Color bgColor, fgColor;
    int bgRGB, fgRGB;

    public IDSCompose(String ids, int size, String filename) {
	//scaler = new AffineTransform();
	charsize = size;

	// Set up the color scheme
	bgColor = Color.white;
	fgColor = Color.black;
	bgRGB = bgColor.getRGB();
	fgRGB = fgColor.getRGB();

	// Decode ideographic description sequence into a tree of IDTree nodes
	idsIndex = 0;
	root = decodeIDS(ids);

	dst = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_BINARY);
	dstgraphics = dst.createGraphics();
	dstgraphics.setColor(bgColor);
	dstgraphics.fill(new Rectangle(0, 0, size, size));
	dstgraphics.setColor(fgColor);

	// Traverse tree and draw character
	dummycanvas = new Canvas();
	traverseIDT(root, 0, 0, size, size, 0);

	try {
	    // Output GIF
	    FileOutputStream out = new FileOutputStream(filename);
	    GifEncoder chargif = new GifEncoder(dst, out);
	    chargif.encode();

	    
	    /* // Output JPEG
	    FileOutputStream out = new FileOutputStream("shufa.jpg");
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(dst);
            param.setQuality(1.0f, false);
            encoder.setJPEGEncodeParam(param);
            encoder.encode(dst);
	    */
	} 
	catch (Exception e) {
	    System.out.println(e);
	}

    }


    public void traverseIDT(IDTree idt, float x, float y, float w, float h, int level) {

	// See the structure of the tree (for debugging)
	for (int i = 0; i < level; i++) {
	    System.out.print("  ");
	}
	System.out.println(Integer.toHexString((int)idt.sinograph) + " " + x + " " + y + " " + " " + w + " " + h);
	//dstgraphics.drawRect((int)x, (int)y, (int)w, (int)h);
	

	// Draw the character
	if (0x3400 <= idt.sinograph && idt.sinograph <= 0x9fa0) {
	    // Set up background of component character
	    Font componentFont = new Font("Dialog", Font.PLAIN, charsize); 
	    //FontMetrics componentMetrics = new FontMetrics(componentFont);
	    Rectangle2D charRect = componentFont.getStringBounds(new Character(idt.sinograph).toString(), 
								 dstgraphics.getFontRenderContext());
	    System.out.println("X: " + charRect.getX() + " Y: " + charRect.getY() + 
			       " W: " + charRect.getWidth() + " H: " + charRect.getHeight() );

	    LineMetrics complm = componentFont.getLineMetrics(new Character(idt.sinograph).toString(), 
								 dstgraphics.getFontRenderContext());

	    System.out.println("Ascent: " + complm.getAscent() + " Descent : " + complm.getDescent());


	    tmpsrc = new BufferedImage((int)charRect.getWidth(), (int)(complm.getAscent() - complm.getDescent()), 
				       BufferedImage.TYPE_BYTE_BINARY);
	    Graphics2D tmpgraphics = tmpsrc.createGraphics();
	    tmpgraphics.setColor(bgColor);
	    tmpgraphics.fill(new Rectangle(0, 0, (int)charRect.getWidth(), (int)(complm.getAscent()- complm.getDescent())));


	    // Draw the component character
	    tmpgraphics.setFont(componentFont);
	    tmpgraphics.setColor(fgColor);
	    tmpgraphics.drawString(new Character(idt.sinograph).toString(), 0, complm.getAscent() - complm.getDescent());

	    // Scale it to fit in the final character
	    tmpdst = tmpsrc.getScaledInstance((int)w, (int)h, Image.SCALE_DEFAULT);
	    BufferedImage tmpdst2 = new BufferedImage((int)w, (int)h, BufferedImage.TYPE_BYTE_BINARY);
	    tmpdst2.createGraphics().drawImage(tmpdst, 0, 0, dummycanvas);

	    // Draw it on the final image, being sure not to erase any previous drawing
	    for (int i = 0; i < tmpdst2.getWidth(); i++) {
		for (int j = 0; j < tmpdst2.getHeight(); j++) {
		    if (tmpdst2.getRGB(i, j) == fgRGB) {
			dst.setRGB((int)x+i, (int)y+j, fgRGB);
		    }
		}
	    }
	    //dstgraphics.drawImage(tmpdst, (int)x, (int)y, dummycanvas);

	} else if (idt.sinograph == 0x2ff0) {
	    traverseIDT(idt.d1, x, y, w/2, h, level+1);
	    traverseIDT(idt.d2, x + w/2, y, w/2, h, level+1);
	} else if (idt.sinograph == 0x2ff1) {
	    traverseIDT(idt.d1, x, y, w, h/2, level+1);
	    traverseIDT(idt.d2, x, y + h/2, w, h/2, level+1);
	} else if (idt.sinograph == 0x2ff2) {
	    traverseIDT(idt.d1, x, y, w/3, h, level+1);
	    traverseIDT(idt.d2, x + w/3, y, w/3, h, level+1);
	    traverseIDT(idt.d3, x + 2*w/3, y, w/3, h, level+1);
	} else if (idt.sinograph == 0x2ff3) {
	    traverseIDT(idt.d1, x, y, w, h/3, level+1);
	    traverseIDT(idt.d2, x, y + h/3, w, h/3, level+1);
	    traverseIDT(idt.d3, x, y + 2*h/3, w, h/3, level+1);
	} else if (idt.sinograph == 0x2ff4) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x + w/5, y + w/5, w * 3/5, h * 3/5, level+1);
	} else if (idt.sinograph == 0x2ff5) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x + w/5, y + 2*w/5, w * 3/5, h * 3/5, level+1);
	} else if (idt.sinograph == 0x2ff6) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x + w/5, y, w * 3/5, h * 4/5, level+1);
	} else if (idt.sinograph == 0x2ff7) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x + w/5, y + w/5, w * 4/5, h * 3/5, level+1);
	} else if (idt.sinograph == 0x2ff8) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x + w/5, y + w/5, w * 4/5, h * 4/5, level+1);
	} else if (idt.sinograph == 0x2ff9) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x, y + w/5, w * 4/5, h * 4/5, level+1);
	} else if (idt.sinograph == 0x2ffa) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x + w/5, y, w * 4/5, h * 4/5, level+1);
	} else if (idt.sinograph == 0x2ffb) {
	    traverseIDT(idt.d1, x, y, w, h, level+1);
	    traverseIDT(idt.d2, x, y, w, h, level+1);
	} 

    }



    public IDTree decodeIDS(String ids) {
	IDTree idnode = new IDTree();

	if ((0x2ff0 <= ids.charAt(idsIndex) && ids.charAt(idsIndex) <= 0x2ff1) ||
	    (0x2ff4 <= ids.charAt(idsIndex) && ids.charAt(idsIndex) <= 0x2ffb)) {
	    // Dual combination

	    idnode.sinograph = ids.charAt(idsIndex);

	    idsIndex++;
	    idnode.d1 = decodeIDS(ids);
	    idnode.d2 = decodeIDS(ids);
	    idnode.d3 = null;

	} else if (0x2ff2 == ids.charAt(idsIndex) || ids.charAt(idsIndex) == 0x2ff3) {
	    // Triple combination
	    idnode.sinograph = ids.charAt(idsIndex);

	    idsIndex++;
	    idnode.d1 = decodeIDS(ids);
	    idnode.d2 = decodeIDS(ids);
	    idnode.d3 = decodeIDS(ids);

	} else if (0x3440 <= ids.charAt(idsIndex) && ids.charAt(idsIndex) <= 0x9f00) {
	    // Ideograph
	    idnode.sinograph = ids.charAt(idsIndex);

	    idsIndex++;
	    idnode.d1 = null;
	    idnode.d2 = null;
	    idnode.d3 = null;
	    
	} else {
	    return null;
	}

	return idnode;
    }


    public static void main(String[] argc) {
	String chao2 = "\u2ff0\u2ff3\u5341\u65e5\u5341\u6708"; // chao2, dynasty
	String guo2  = "\u2ff4\u56d7\u7389"; // guo2, simplified country
	String zhe4  = "\u2ffa\u8fb6\u8a00"; // zhe4, (traditional) "this"
	String jian1 = "\u2ff5\u9580\u65e5"; // jian1, (traditional) "indirect"
	String de5   = "\u2FF0\u767D\u2FF9\u52F9\u4E36";

	//new IDSCompose(de5, 192);

	String idsline, filename, idsstring;
	int currentindex = 0;
	try {
	    FileInputStream idsin = new FileInputStream("ids.txt");
	    BufferedReader r = new BufferedReader(new InputStreamReader(idsin, "UTF8"));
	    while ((idsline = r.readLine()) != null) {
		filename = Integer.toHexString(idsline.charAt(0)).toUpperCase() + ".gif";
		if (idsline.length() > 2) {
		    System.out.println("Composing " + filename + " " + currentindex);
		    idsstring = idsline.substring(2, idsline.length());
		    
		    new IDSCompose(idsstring, 192, filename);
		}
		currentindex++;
	    }
	    idsin.close();
	} 
	catch (Exception ioexc) {
	    System.out.println("Error! " + ioexc);
	}

	System.exit(0);
    }

}


class IDTree {
    public char   sinograph;
    public IDTree d1, d2, d3;

    public IDTree() {
	sinograph = '\u0000';
	d1 = null; d2 = null; d3 = null;
    }
}

