package pbl2011.mvc;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import pbl2011.common.CommonConst;
import pbl2011.gui.State;
import pbl2011.model.AssociationArc;
import pbl2011.model.ClassNode;
import pbl2011.model.NodeArcSet;

/**
 * <p>
 * 操作中オブジェクト管理
 * <p>
 * 操作中オブジェクトの管理を担当するクラス。
 *
 * @author 10745104 Y.Ishii
 */
public class CurrentManager implements CommonConst {

	public Model model;
	public CurrentManager(Model m) {
		model = m;
		selectedClasses = new ArrayList<ClassNode>();
		clipBoardC = new ArrayList<ClassNode>();
		clipBoardA = new ArrayList<AssociationArc>();
	}

	/**
	 * <p>
	 * 選択中クラス１件取得
	 * １つ選択している場合のみ、データを返却
	 */
	public ClassNode getCurrentNode() {
		if (selectedClasses.size() == 1) {
			return selectedClasses.get(0);
		}
		return null;
	}

	/**
	 * <p>
	 * クラス１件選択
	 */
	public void setCurrentNode(ClassNode current) {
		selectedClasses.clear();
		selectedClasses.add(current);
		currentArc = null;
		model.changeModel();
	}

	/** 操作中の関連 */
	protected AssociationArc currentArc;

	public AssociationArc getCurrentArc() {
		return currentArc;
	}

	public void setCurrentArc(AssociationArc currentArc) {
		this.currentArc = currentArc;
		selectedClasses.clear();
		model.changeModel();
	}

	public void clear() {
		currentArc = null;
		selectedClasses.clear();
	}

	/** 選択中の範囲 */
	private Point sPoint;
	private Point ePoint;

	public void setStartPoint(Point p ) {
		clear();
		sPoint = p;
		ePoint = p;
	}
	public void setEndPoint(Point p ) {
		ePoint = p;
	}

	public Rectangle getSelectRect() {

		return calcRect(sPoint,ePoint);
	}
	public void selectStatus() {
		if(sPoint == null || ePoint == null) {
			return;
		}
		Rectangle rect = calcRect(sPoint, ePoint);

		ArrayList<ClassNode> allclass = model.getAllActiveClassData();
		ArrayList<ClassNode> selClass = new ArrayList<ClassNode>();
		for(ClassNode cn : allclass ){

			if(rect.contains(cn.getRectangle())) {
				selClass.add(cn);
			}
		}

		addCurrentClasses(selClass);
		sPoint = null;
		ePoint = null;
	}

	private Rectangle calcRect(Point p1, Point p2) {
		if (p1 == null || p2 == null) {
			return null;
		}

		int w ,h, x, y;
		if(p2.x > p1.x) {
			w = p2.x - p1.x;
			x = p1.x;
		} else {
			w =  p1.x - p2.x;
			x = p2.x;
		}

		if(p2.y > p1.y) {
			h =  p2.y - p1.y;
			y = p1.y;
		} else {
			h =  p1.y - p2.y;
			y = p2.y;
		}
		return  new Rectangle(x,y,w,h);
	}

	/** 選択中のクラスリスト */
	private ArrayList<ClassNode> selectedClasses;

	public ArrayList<ClassNode> getCurrentNodeList() {
		return selectedClasses;
	}

	/**
	 * <p>
	 * 選択中クラス追加
	 */
	public void addCurrentClasses(ArrayList<ClassNode> newClasses) {
		selectedClasses.addAll((ArrayList<ClassNode>) newClasses);
		model.changeModel();
	}

	/**
	 * <p>
	 * 選択中クラス追加
	 */
	public void addCurrentClasses(ClassNode newClass) {
		selectedClasses.add(newClass);
		model.changeModel();
	}

	/** クリップボード */
	protected ArrayList<ClassNode> clipBoardC;
	protected ArrayList<AssociationArc> clipBoardA;

	/**
	 * コピー
	 * 選択中のクラス、選択中のクラス同士を結ぶ関連の
	 * クローンを作成しクリップボードにコピーする。
	 *
	 */
	public void copy() {
		clipBoardC.clear();
		clipBoardA.clear();

		// クラスコピー
		Map<Integer, Integer> idMap = new HashMap<Integer, Integer>();
		for ( ClassNode cn :getCurrentNodeList()) {
			int newid = model.getNextClassId();

			idMap.put(cn.classId, newid);
			ClassNode copy = (ClassNode) cn.clone();
			copy.classId = newid;
			copy.clearUndoRedoModel();
			clipBoardC.add(copy);
		}
		ArrayList<Integer> list = new ArrayList<Integer>();
		for(ClassNode cn : selectedClasses) {
			list.add(cn.classId);
		}

		// 関連コピー
		for (AssociationArc arc : model.getAllActiveAssociationArc()) {
			if (list.contains(arc.classIdSource)  &&
					list.contains(arc.classIdTarget)){
				int newid = model.getNewAssocId();
				
				AssociationArc copy = (AssociationArc) arc.clone();
				copy.classIdSource = idMap.get(arc.classIdSource);
				copy.classIdTarget = idMap.get(arc.classIdTarget);
				copy.assocId = newid;
				copy.clearUndoRedoModel();
				clipBoardA.add(copy);
			}
		}
	}

	/**
	 * ペースト
	 * クリップボードのオブジェクトをモデルに追加する。
	 * 元の位置より、右下に貼り付ける。
	 */
	public void paste() {
		clear();
		NodeArcSet nodearcSet = new NodeArcSet();
		nodearcSet.setCreateFlg(true);
		for (ClassNode cn : clipBoardC) {
			cn.moveDelta(48, 48, State.width, State.height);
		}
		addCurrentClasses(clipBoardC);
		model.addClass(clipBoardC);
		if (clipBoardC instanceof ArrayList<?>) {
			for (ClassNode cn: (ArrayList<ClassNode>)clipBoardC) {
				nodearcSet.add(cn);
			}
		}
		clipBoardC.clear();

		model.addAssociation(clipBoardA);
		if (clipBoardA instanceof ArrayList<?>) {
			for(AssociationArc aa: (ArrayList<AssociationArc>)clipBoardA) {
				nodearcSet.add(aa);
			}
		}
		clipBoardA.clear();
		model.undoredoModel.tryPush(nodearcSet);
	}

	/**
	 * カット
	 * コピーメソッドを呼び出した後、選択中のクラスを削除する。
	 */
	public void cut() {
		copy();
		model.deleteClass();
	}
	
	/**
	 * アンドゥー
	 * 履歴スタックに保存されたデータから、アンドゥーを適用する。
	 */
	public void undo() {
		boolean isCreate = false;

		if (model.undoredoModel.canUndo()) {
			isCreate = model.undoredoModel.getNodeArcSetLatest().isCreate();

			for (ClassNode cn: model.undoredoModel.getNodeArcSetLatest().classNodeList) {
				if (cn.undoredoModel.canUndo()) {
					if (isCreate) {
						model.removeClassById(cn.undoredoModel.getNodeLatest());
					}
					cn.undoredoModel.undoAction();
				}
				if (cn.undoredoModel.canUndo() && !isCreate) {
					model.setClassDataById(cn.undoredoModel.getNodeLatest());
				}
			}

			for (AssociationArc aa: model.undoredoModel.getNodeArcSetLatest().associationArcList) {
				if (aa.undoredoModel.canUndo()) {
					if (isCreate) {
						model.removeArcById(aa.undoredoModel.getArcLatest());
					}
					aa.undoredoModel.undoAction();
				}
				
//				System.out.println(aa);
				if (aa.undoredoModel.canUndo() && !isCreate) {
//					System.out.println(aa.undoredoModel.getAssociationArcList());
//					System.out.println(aa.undoredoModel.getArcLatest());
					model.setArcDataById(aa.undoredoModel.getArcLatest());
				}
			}
			
			model.undoredoModel.undoAction();

		}
		clear();
		model.changeModel();
	}
	
	/**
	 * リドゥー
	 * 履歴スタックに保存されたデータから、リドゥーを適用する。
	 */
	public void redo() {
		boolean isCreate = false;
	
		if (model.undoredoModel.canRedo()) {
			model.undoredoModel.redoAction();
			isCreate = model.undoredoModel.getNodeArcSetLatest().isCreate();
			
			for (ClassNode cn: model.undoredoModel.getNodeArcSetLatest().classNodeList) {
				if (cn.undoredoModel.canRedo()) {
					cn.undoredoModel.redoAction();
					if (isCreate) {
						model.addClass(cn.undoredoModel.getNodeLatest().clone());
					} else {
						model.setClassDataById(cn.undoredoModel.getNodeLatest());
					}
				}
			}
				
			for (AssociationArc aa: model.undoredoModel.getNodeArcSetLatest().associationArcList) {
				if (aa.undoredoModel.canRedo()) {
					aa.undoredoModel.redoAction();
					if (isCreate) {
						model.addAssociation(aa.undoredoModel.getArcLatest().clone());
					} else {
						model.setArcDataById(aa.undoredoModel.getArcLatest());				
					}

				}
			}
		}
		clear();
		model.changeModel();
	}
}
