package pbl2011.common;

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


/**
 * 共通ユーティリティクラス
 * <p>
 *
 * @author 10745104 Y.Ishii
 *
 */
public class Util implements CommonConst {

	/**
	 * 矢印を描くためのポイントを算出する
	 * <p>
	 *
	 * @param p1
	 *            矢印のポイント
	 * @param p2
	 *            矢印と反対側のポイント
	 * @return int配列（X座標,Y座標,X座標,Y座標）
	 */
	public static int[] calcArrowPoint(Point p1, Point p2) {

		double theta = Math.atan2((p2.y - p1.y), (p2.x - p1.x));
		double theta2 = ARROW_ANGLE_A;

		int x1 = (int) (ARROW_SIZE_A * Math.cos(theta + theta2));
		int y1 = (int) (ARROW_SIZE_A * Math.sin(theta + theta2));
		int x2 = (int) (ARROW_SIZE_A * Math.cos(theta - theta2));
		int y2 = (int) (ARROW_SIZE_A * Math.sin(theta - theta2));

		return new int[] { p1.x + x1, p1.y + y1, p1.x + x2, p1.y + y2 };
	}

	/**
	 * 継承の三角形を描くためのポイントを算出する
	 * <p>
	 *
	 * @param p1
	 *            矢印のポイント
	 * @param p2
	 *            矢印と反対側のポイント
	 * @return int配列（X座標,Y座標,X座標,Y座標）
	 */
	public static int[] calcTrianglePoint(Point p1, Point p2) {

		double theta = Math.atan2((p2.y - p1.y), (p2.x - p1.x));
		double theta2 = ARROW_ANGLE_E;

		int x1 = (int) (ARROW_SIZE_E * Math.cos(theta + theta2));
		int y1 = (int) (ARROW_SIZE_E * Math.sin(theta + theta2));
		int x2 = (int) (ARROW_SIZE_E * Math.cos(theta - theta2));
		int y2 = (int) (ARROW_SIZE_E * Math.sin(theta - theta2));

		return new int[] { p1.x + x1, p1.y + y1, p1.x + x2, p1.y + y2 };
	}

	/**
	 * クラスと関連の線が交差するポイントを求める
	 * <p>
	 *
	 * @param rect
	 *            クラス
	 * @param p
	 *            反対側のクラスの中心点
	 * @return 交差点
	 */
	public static Point calcCrossPoint(Rectangle rect, Point p) {

		// 矩形の中にポイントがある場合は対象外
		if (rect.contains(p)) {
			return null;
		}

		// 矩形の縦と交差する
		int x1 = rect.width / 2;
		int x2 = p.x - (rect.x + (rect.width / 2));
		int y2 = (rect.y + (rect.height / 2)) - p.y;
		int y1;
		if (x2 == 0) {
			y1 = 0;
		} else {
			y1 = y2 * x1 / x2;
		}
		if (x2 != 0 && y1 <= (rect.height / 2)) {

			if (p.x > rect.x) {
				int cy = rect.y + (rect.height / 2) - y1;
				if (cy >= rect.y && cy <= (rect.y + rect.height)) {
					return new Point((rect.x + rect.width), cy);
				}
			} else {
				int cy = rect.y + (rect.height / 2) + y1;
				if (cy >= rect.y && cy <= (rect.y + rect.height)) {
					return new Point((rect.x), cy);
				}
			}
		}

		// 矩形の横と交差する
		x1 = rect.height / 2;
		x2 = p.y - (rect.y + (rect.height / 2));
		y2 = (rect.x + (rect.width / 2)) - p.x;
		y1 = y2 * x1 / x2;

		if (y1 <= (rect.width / 2)) {

			if (p.y > rect.y) {
				int cx = rect.x + (rect.width / 2) - y1;
				if (cx >= rect.x && cx <= (rect.x + rect.width)) {
					return new Point(cx, (rect.y + rect.height));
				}
			} else {
				int cx = rect.x + (rect.width / 2) + y1;
				if (cx >= rect.x && cx <= (rect.x + rect.width)) {
					return new Point(cx, (rect.y));
				}
			}
		}

		return null;
	}

	/**
	 * クラスとクラスを直角に結ぶポイントを求める(横優先）
	 * <p>
	 * @param rect1 クラス
	 * @param rect2 クラス
	 * @return 関連線のポイントリスト
	 */
	public static Point[] calcRightAngleHPoint(Rectangle rect1, Rectangle rect2) {

		// 矩形と矩形が重なる場合は対象外
		if (rect1.intersects(rect2)) {
			return null;
		}

		// どちらの矩形が上にあるか
		boolean flg;
		Rectangle left;
		Rectangle right;
		if (rect1.x <= rect2.x) {
			left = rect1;
			right = rect2;
			flg = true;
		} else {
			left = rect2;
			right = rect1;
			flg = false;
		}
		// 上下を結ぶ幅がない場合は、左右を結ぶ
		if( left.x + left.width >= right.x) {
			Rectangle upper;
			Rectangle lower;
			if (rect1.y <= rect2.y) {
				upper = rect1;
				lower = rect2;
				flg = true;
			} else {
				upper = rect2;
				lower = rect1;
				flg = false;
			}

			return putUpAndDown(upper,lower,flg);
		}
		return putRightAndLeft(left,right,flg);

	}

	/**
	 * クラスとクラスを直角に結ぶポイントを求める(縦優先）
	 * <p>
	 * @param rect1 クラス
	 * @param rect2 クラス
	 * @return 関連線のポイントリスト
	 */
	public static Point[] calcRightAngleVPoint(Rectangle rect1, Rectangle rect2) {

		// 矩形と矩形が重なる場合は対象外
		if (rect1.intersects(rect2)) {
			return null;
		}

		// どちらの矩形が上にあるか
		boolean flg;
		Rectangle upper;
		Rectangle lower;
		if (rect1.y <= rect2.y) {
			upper = rect1;
			lower = rect2;
			flg = true;
		} else {
			upper = rect2;
			lower = rect1;
			flg = false;
		}

		// 上下を結ぶ幅がない場合は、左右を結ぶ
		if( upper.y + upper.height >= lower.y) {
			Rectangle left;
			Rectangle right;
			if (rect1.x <= rect2.x) {
				left = rect1;
				right = rect2;
				flg = true;
			} else {
				left = rect2;
				right = rect1;
				flg = false;
			}
			return putRightAndLeft(left,right,flg);
		}
		return putUpAndDown(upper,lower,flg);

	}

	/**
	 * クラスとクラスを直角に結ぶポイントを求める（上下に配置）
	 * <p>
	 * @param upper クラス
	 * @param lower クラス
	 * @param flg フラグ(true:upperからlowerへの線　false:lowerからupperへの線)
	 * @return 関連線のポイントリスト
	 */
	private static Point[] putUpAndDown(Rectangle upper, Rectangle lower, boolean flg) {
		int a = (lower.y - ( upper.y + upper.height )) / 2 + (upper.y +upper.height) ;
		Point p1 = new Point(upper.x + ( upper.width / 2 ), upper.y + upper.height );
		Point p2 = new Point(upper.x + ( upper.width / 2 ), a );
		Point p3 = new Point(lower.x + ( lower.width / 2 ), a );
		Point p4 = new Point(lower.x + ( lower.width / 2 ), lower.y );
		if (flg) {
			return new Point[]{p1, p2, p3, p4 };
		}
		return  new Point[]{p4, p3, p2, p1 };
	}
	/**
	 * クラスとクラスを直角に結ぶポイントを求める（左右に配置）
	 * <p>
	 * @param left クラス
	 * @param right クラス
	 * @param flg フラグ(true:leftからrightへの線　false:rightからleftへの線)
	 * @return 関連線のポイントリスト
	 */
	private static Point[] putRightAndLeft(Rectangle left, Rectangle right, boolean flg) {
		int a = (right.x - ( left.x + left.width )) / 2 + ( left.x + left.width ) ;
		Point p1 = new Point((left.x + left.width ), (left.y + (left.height / 2 )));
		Point p2 = new Point( a, (left.y + (left.height / 2 )));
		Point p3 = new Point( a, (right.y + (right.height / 2 )));
		Point p4 = new Point( right.x, (right.y + (right.height / 2 )));
		if (flg) {
			return new Point[]{p1, p2, p3, p4 };
		}
		return  new Point[]{p4, p3, p2, p1 };
	}

	/**
	 * グリッドサイズに合わせる
	 * <p>
	 *
	 * @param
	 * @return
	 */
	public static Point adjust(Point pt) {
		return new Point(adjust(pt.x), adjust(pt.y));
	}


	/**
	 * グリッドサイズに合わせる
	 * <p>
	 *
	 * @param
	 * @return
	 */
	public static int adjust(int i) {
		return adjust(i, CommonConst.GRID_SIZE);
	}

	/**
	 * グリッドサイズに合わせる
	 * <p>
	 *
	 * @param
	 * @return
	 */
	public static int adjust(int i, int gridSize) {
		return (i + (gridSize / 2)) / gridSize * gridSize;
	}


	/**
	 * デバッグ用
	 * <p>
	 */
	public static boolean debug(String str) {
		System.err.println(str);
		return true;
	}
	
	private static final String REG_EX_JAVA_SPECIFIC_SYMBOL = ".*[\\[\\]\\<,\\>\\.+].*";
	/**
	 * "[","]",",","<",">","."等の記号を持つクラス型名かどうかを判定する
	 * @param type typeID or TypeName
	 * @return 記号を持つ：true、記号を持たない:false
	 */
	public static boolean hasSymbol(String type) {
		return type.matches(REG_EX_JAVA_SPECIFIC_SYMBOL);
	}

	public static Integer removeSymbol(String typeId) {
		String tmpTypeId = typeId;
		tmpTypeId = tmpTypeId.replace("\\[.*\\]", "");
		tmpTypeId = tmpTypeId.replace("\\<.*\\>", "");
		tmpTypeId = tmpTypeId.replace("\\.+", "");
		return Integer.valueOf(tmpTypeId);
	}
	/**
	 * 引数で与えられたtypeに含まれるtypeをすべて返却する
	 * @param type typeName or typeId
	 * @return　型名のリスト
	 */
	public static List<String> getTypeList(String type){
		List<String> list = new ArrayList<String>();
		String tmpType = type.replace(SPACE, ""); // スペースは除去
		if (hasSymbol(tmpType)) {
			String[] strs = tmpType.split("[\\[\\]\\<,\\>\\.+]");
				for (String s : strs) {
					if (!"".equals(s)) { //空文字は除去
						list.add(s);
					}
				}
		} else {
			list.add(type);
		}
		return list;
	}
	
	/**
	 * クラスローダの取得
	 * @param currentClass 呼び出し元のクラス
	 * @return クラスローダ
	 */
	public static ClassLoader getClassLoader(Class<?> currentClass){
	    ClassLoader cloader = Thread.currentThread().getContextClassLoader();
	    if (cloader == null) {
	    	cloader = currentClass.getClassLoader();
	    }
	    return cloader;
	}
}
