package pbl2011.model;

import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;

import pbl2011.common.CommonConst;
import pbl2011.common.Util;
import pbl2011.gui.State;

/**
 * クラスノード
 * <p>
 *
 * @author 10745104 Y.Ishii
 *
 */
public abstract class ClassNode implements CommonConst, Cloneable {

	/** クラスID */
	public int classId;

	/** パッケージID */
	public int packageId;

 	/** クラスタイプ */
	public ClassType classType = ClassType.CLASS;

	/** クラス名前 */
	public String className;

	/** クラス可視性 */
	public VisibilityType visibility;

	/** メソッドリスト */
	public List<Method> methodList = new ArrayList<Method>();

	/** 属性リスト */
	public List<Attribute> attributeList = new ArrayList<Attribute>();

	/** 削除フラグ */
	public boolean delFlag;

	/** 定義済みクラスフラグ */
	public boolean isBuiltInClass;

	/** 位置 */
	public Point p;

	/** 高さ１ */
	public int height1;

	/** 高さ２ */
	public int height2;

	/** 高さ３ */
	public int height3;

	/** 幅 */
	public int width;
	
	/** 色(red) */
	public int colorRed;
	
	/** 色(green) */
	public int colorGreen;
	
	/** 色(blue) */
	public int colorBlue;

	/** UndoRedoモデル **/
	public UndoRedoModel undoredoModel = new UndoRedoModel();

	/** 高さ取得 */
	public int height() {
		return height1 + height2 + height3;
	}

	public abstract String toString();

	public void moveDelta(int dx, int dy, int w, int h) {
		p.x = p.x + dx;
		p.y = p.y + dy;
		if (p.x < 0) {
			p.x = 0;
		} else if (p.x + width > w ) {
			p.x = w - width;
		}
		if (p.y < 0) {
			p.y = 0;
		} else if (p.y + height() > h ) {
			p.y = h - height();
		}

	}

	public String getPackageName() {
		return PackageManager.getPackageName(packageId);
	}

	public void resize(int x, int y, int width, int height1, int height2,
			int height3) {

		this.width = width;
		this.p.x = x;
		this.p.y = y;
		this.height1 = height1;
		this.height2 = height2;
		this.height3 = height3;

	}

	// 操作追加
	public void addMethod() {

		methodList.add(new Method("new method", String.valueOf(DEFAULT_RETURN_TYPE_ID), VisibilityType.PUBLIC));
		width = calcMinWidth();
		height3 = calcMinHeigh3();
	}

	// 操作変更
	public void changeMethod(int sc, String name, String dataTypeId, VisibilityType visi) {
		Method m = methodList.get(sc);
		m.change(name, dataTypeId, visi);
	}

	// 操作削除
	public void delMethod(int i) {
		methodList.remove(i);
	}

	// 操作順序変更
	public void changeMethodSeq(int i, boolean direction) {

		if (direction && i > 0) {
			Method m = methodList.remove(i);
			methodList.add(i - 1, m);
		} else if (!direction && i < methodList.size() - 1) {
			Method m = methodList.remove(i);
			methodList.add(i + 1, m);
		}
	}

	// 属性追加
	public void addAttribute() {
		attributeList.add(new Attribute("new attribute", String.valueOf(DEFAULT_DATA_TYPE_ID), VisibilityType.PRIVATE));
		width = calcMinWidth();
		height2 = calcMinHeigh2();
	}

	// 属性変更
	public void changeAttribute(int sc, String name, String type, VisibilityType visi) {
		Attribute a = attributeList.get(sc);
		a.change(name, type, visi);
	}

	// 属性削除
	public void delAttribute(int i) {
		attributeList.remove(i);
	}

	// 属性順序変更
	public void changeAttributeSeq(int i, boolean direction) {
		if (direction && i > 0) {
			Attribute a = attributeList.remove(i);
			attributeList.add(i - 1, a);
		} else if (!direction && i < attributeList.size() - 1) {
			Attribute a = attributeList.remove(i);
			attributeList.add(i + 1, a);
		}
	}

	// 引数変更
	public void changeParameter(int no, int sc, String name, String typeId) {
		Method m = methodList.get(no);
		m.changeParameter(sc, name, typeId);
	}

	// 引数追加
	public void addParameter(int no) {
		if(methodList.size() > no) {
			Method m = methodList.get(no);
			m.addParameter(classId);
		}
	}

	// 引数削除
	public void deleteParameter(int methodNo, int selectedLine) {

		if(methodList.size() > methodNo && (methodList.get(methodNo)).parameterList.size() > selectedLine) {
			(methodList.get(methodNo)).parameterList
				.remove(selectedLine);
		}
	}

	// 引数順序変更
	public void changeParameterSeq(int methodNo, int selectedLine,
			boolean direction) {

		if (direction && selectedLine > 0) {
			Parameter p = (methodList.get(methodNo)).parameterList
					.remove(selectedLine);
			(methodList.get(methodNo)).parameterList.add(
					selectedLine - 1, p);
		} else if (!direction && selectedLine > -1
				&& selectedLine < (methodList.get(methodNo)).parameterList
						.size() - 1) {
			Parameter p = (methodList.get(methodNo)).parameterList
					.remove(selectedLine);
			(methodList.get(methodNo)).parameterList.add(
					selectedLine + 1, p);
		}
	}

	public String visibilityMark() {
		return visibility.mark();
	}

	/**
	 * クラスを矩形として返す
	 * <p>
	 *
	 * @param c
	 *            クラス
	 * @return
	 */
	public Rectangle getRectangle() {

		Dimension d = new Dimension(width, height());
		return new Rectangle(p, d);
	}

	/**
	 * クラスの中心点の座標を求める
	 * <p>
	 *
	 * @param c
	 *            クラス
	 * @return 中心の座標
	 */
	public Point getCenter() {

		return new Point((p.x + (width / 2)), (p.y + (height() / 2)));
	}

 	public String typeMark() {
 		return classType.mark();
 	}

	public boolean isClassNode() {
		return classType.equals(ClassType.CLASS) || classType.equals(ClassType.ABSTRACT_CLASS);
	}
	public boolean isInterfaceNode() {
		return classType.equals(ClassType.INTERFACE);
	}

	public String getPackagePhrase() {
		if (packageId != PACKAGE_DEFAULT_ID) {
			return PackageType.PACKAGE.code() + SPACE + getPackageName() + SEMI_COLON;
		} else {
			return "";
		}
	}

	public String getClassdeclarationPhrase() {
		return (NULL.equals(visibility) ? "": visibility.code()) + SPACE + classType.code() + SPACE + className;
	}

	public void autoAdjust() {
		width = calcMinWidth();
		height1 = calcMinHeigh1();
		height2 = calcMinHeigh2();
		height3 = calcMinHeigh3();
	}

	public int calcMinWidth() {

		FontMetrics fm = State.fm;
		int len = fm.stringWidth(visibilityMark() + typeMark() + " "+ className);
		for (Attribute a : attributeList) {
			if(a.visibility.level() <= State.attributeVisibility) {
				int i = fm.stringWidth(a.visibilityMark() + a.attributeName);
				if (i > len)
					len = i;
			}
		}

		for (Method m : methodList) {
			if(m.visibility.level() <= State.methodVisibility) {
				int i = fm.stringWidth(m.visibilityMark() + m.methodName);
				if (i > len)
					len = i;
				}
		}

		return Util.adjust(len + SIDE_MARGINE * 2);
	}

	public int calcMinHeigh1() {

		int len = State.fm.getHeight()+ LINE_SPACE;
		return Util.adjust(len + LINE_MARGINE * 2);
	}

	public int calcMinHeigh2() {
		int cnt = 0;
		for (Attribute a : attributeList) {
			if(a.visibility.level() <= State.attributeVisibility) {
				cnt++;
			}
		}
		int len = (State.fm.getHeight() + LINE_SPACE) * cnt;
		return Util.adjust(len + LINE_MARGINE * 2);
	}

	public int calcMinHeigh3() {

		int cnt = 0;
		for (Method a : methodList) {
			if(a.visibility.level() <= State.methodVisibility) {
				cnt++;
			}
		}
		int len = (State.fm.getHeight() + LINE_SPACE) * cnt;
		return Util.adjust(len + LINE_MARGINE * 2);
	}

	public void clearUndoRedoModel() {
		this.undoredoModel = new UndoRedoModel();
	}

	public ClassNode clone() {
		try {
			return (ClassNode)super.clone();
		} catch (CloneNotSupportedException e) {
			throw new InternalError();
		}
	}

	public boolean equals(Object o) {

		if (o == null) {
			return false;
		}

		ClassNode cn = (ClassNode)o;

		if (this.classId != cn.classId) {
			return false;
		}

		if (this.packageId != cn.packageId) {
			return false;
		}

		if (!this.classType.equals(cn.classType)) {
			return false;
		}

		if (!this.className.equals(cn.className)) {
			return false;
		}

		if (!this.visibility.equals(cn.visibility)) {
			return false;
		}

		if (!this.methodList.equals(cn.methodList)) {
			return false;
		}

		if (!this.attributeList.equals(cn.attributeList)) {
			return false;
		}

		if (this.delFlag != cn.delFlag) {
			return false;
		}

		if (!this.p.equals(cn.p)) {
			return false;
		}

		if (this.height1 != cn.height1) {
			return false;
		}

		if (this.height2 != cn.height2) {
			return false;
		}

		if (this.height3 != cn.height3) {
			return false;
		}

		if (this.width != cn.width) {
			return false;
		}
		
		if (this.colorRed != cn.colorRed){
			return false;
		}
		
		if (this.colorGreen != cn.colorGreen){
			return false;
		}
		
		if (this.colorBlue != cn.colorBlue){
			return false;
		}

		return true;
	}

}
