package ash.reverse.uml;
import java.util.List;
import java.util.ArrayList;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.awt.FontMetrics;
import java.awt.Rectangle;

class Node implements UMLConst {
	final static int WIDTH_MARGIN = 20;
	final static int MIN_WIDTH = 64;
	final static int MAX_WIDTH = 256;
	protected int packageID;
	private String name;
	protected String info;
	private int width = -1;
	FontMetrics fm = DEFAULT_FONTMETRICS;

	Node(String name, String info, int packageID) {
		this.name = name;
		this.info = info;
		this.packageID = packageID;
	}
	void setFontMetrics(FontMetrics fm) {
		if(fm != null) this.fm = fm;
	}

	void print(PrintStream out) {
		String line = info;
		if(packageID != 0) line += SEP + packageID;
		out.println(line);
	}

	int width() {
		if(width < 0) {
			width = upAdjust(fm.stringWidth(name) + WIDTH_MARGIN);
		}
		return adjust(width, MIN_WIDTH, MAX_WIDTH);
	}

	int adjust(int z, int min, int max) {
		z = adjust(z);
		return Math.max(Math.min(z,max), min);
	}
	int adjust(int z) {
		return (z + (UNIT / 2)) / UNIT * UNIT;
	}
	int upAdjust(int z) {
		return (z + (UNIT - 1)) / UNIT * UNIT;
	}
}

class ClassNode extends Node {
	private List<Node> attributes = new ArrayList<Node>();
	private List<MethodNode> methods = new ArrayList<MethodNode>();
	private int x;
	private int y;

	ClassNode(String name, String info, int packageID) {
		super(name, info, packageID);
	}

	void addAttribute(String name, String info, int packageID) {
		attributes.add(new Node(name, info, packageID));
	}
	void addMethod(MethodNode method) { methods.add(method); }

	void print(PrintStream out) {
		String line = info + layoutInfo();
		if(packageID != 0) line += SEP + packageID;
		out.println(line);
		for(Node a : attributes) a.print(out);
		for(Node m : methods) m.print(out);
	}
	private String layoutInfo() {
		return
			SEP + x + SEP + y +
			SEP + height1() + SEP + height2() + SEP + height3() +
			SEP + width();
	}

	int height() {
		return height1() + height2() + height3();
	}
	private int height1() { return height(1); }
	private int height2() { return height(attributes.size()); }
	private int height3() { return height(methods.size()); }
	private int height(int n) {
		return Math.max(UNIT, upAdjust(n * fm.getHeight() + 4));
	}

	@Override int width() {
		int maxWidth = super.width();
		for(Node n : attributes) {
			int w = n.width();
			if(w > maxWidth) maxWidth = w;
		}
		for(Node n : methods) {
			int w = n.width();
			if(w > maxWidth) maxWidth = w;
		}
		return upAdjust(maxWidth);
	}

	void setLocation(int x, int y) {
		this.x = x;
		this.y = y;
	}

	Rectangle bounds() {
		return new Rectangle(x, y, width(), height());
	}
}

class MethodNode extends Node {
	private List<Node> params = new ArrayList<Node>();
	MethodNode(String name, String info, int packageID) {
		super(name, info, packageID);
	}
	void addParam(String name, String info, int packageID) {
		params.add(new Node(name, info.replace('+', ' '), packageID));
	}
	void print(PrintStream out) {
		super.print(out);
		for(Node p : params) p.print(out);
	}
}
