/*
 * Created on 2007/03/12
 *
 *
 * Copyright(c) 2007 Yoshimasa Matsumoto
 */
package netjfwatcher.nodemap.gui;

import java.util.ArrayList;

import netjfwatcher.engine.nodemaneger.map.NodeMapInfo;
import netjfwatcher.engine.nodemaneger.map.NodeMapMenuNodeOperation;
import netjfwatcher.engine.socket.info.NodeInformation;
import netjfwatcher.engine.socket.info.NodeMapLinkInformation;
import netjfwatcher.engine.view.editorpart.common.EditorInputNodeView;
import netjfwatcher.engine.view.editorpart.node.EditorNodeOperation;
import netjfwatcher.nodemap.gui.icon.NodeIcon;

import org.eclipse.jface.action.SubStatusLineManager;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

/**
 * ノードMap ViewのCanvas上のマウスリスナーを実装するクラスです。
 * 
 * @author Yoshimasa Matsumoto
 * @version 1.0
 */
public class NodeMapDrawViewMouseListener extends MouseAdapter implements
		MouseMoveListener {
	/* Composite */
	private Composite parent;

	/* GC */
	private GC gc;

	/* ノードIcon情報リスト */
	private ArrayList<NodeMapInfo> nodeMapNodeInfoList;

	/* ノードリンク情報 */
	private NodeMapLinkInformation nodMapLinkInformationList;

	/* Link開始座標 */
	private int lineStartX;

	private int lineStartY;

	/* Link開始ノード */
	private NodeInformation startNode;

	/* Link描画フラグ(popUpメニューでセット) */
	private boolean isLineSelected = false;

	/* Link描画開始フラグ */
	private boolean isLineStart = false;

	/* ノードMapビュー描画クラスインスタンス */
	private NodeMapDrawViewDraw mapDraw;

	/* ノードMapビューPopUpメニュークラスインスタンス */
	private NodeMapDrawViewPopUpMenu popMenu;

	/**
	 * マウスリスナークラスインスタンスを生成します。
	 * 
	 * @param display
	 *            Display
	 * @param parent
	 *            Composite
	 * @param canvas
	 *            Canvas
	 * @param gc
	 *            GC
	 * @param statusLineManager
	 *            ステータスラインマネージャ
	 * @param isSocket
	 *            RCPクライアントとしてSocketにてサーバと通信を行うかのフラグ
	 */
	public NodeMapDrawViewMouseListener(ScrolledComposite scrollComposite,
			Display display, final Composite parent, Canvas canvas, GC gc,
			SubStatusLineManager statusLineManager, boolean isSocket) {
		this.gc = gc;
		this.parent = parent;
		mapDraw = new NodeMapDrawViewDraw(scrollComposite, canvas, gc);

		if (isSocket) {
			popMenu = new NodeMapDrawViewPopUpMenuSocket(display, canvas,
					mapDraw, this, statusLineManager);
		} else {
			popMenu = new NodeMapDrawViewPopUpMenu(display, canvas, mapDraw,
					this, statusLineManager);
		}
		loadNodeMapInfo();
	}

	private void loadNodeMapInfo() {
		nodeMapNodeInfoList = popMenu.getNodeMapNodeInfoList();
		nodMapLinkInformationList = popMenu.getNodMapLinkInformationList();

	}

	/**
	 * マウスDownイベントリスナーです。 ノードIconの移動または、ノード間リンク描画を開始します。
	 * 
	 * @param event
	 *            MouseEvent
	 */
	public void mouseDown(MouseEvent event) {
		// ノードの複数選択防止フラグ
		boolean isFoundSelectedNode = false;
		popMenu.setPopUpMenu(parent);

		for (int i = 0; i < nodeMapNodeInfoList.size(); i++) {
			NodeMapInfo nodeMapInfo = (NodeMapInfo) nodeMapNodeInfoList.get(i);

			if (!isFoundSelectedNode
					&& ((event.x > nodeMapInfo.getX()) && (event.x < (nodeMapInfo
							.getX() + nodeMapInfo.getWidth())))
					&& ((event.y > nodeMapInfo.getY()) && (event.y < (nodeMapInfo
							.getY() + nodeMapInfo.getHeight())))) {
				/*
				 * MouseDown位置がノード
				 */
				if (!isLineSelected) {
					/*
					 * Link描画モードでなく、ノード 移動モード
					 */
					if (!nodeMapInfo.getNodeInfo().getIpaddress().equals("")) {
						popMenu.setNodeMenu(nodeMapInfo.getNodeInfo(), true);
					} else {
						popMenu.setNodeMenu(nodeMapInfo.getNodeInfo(), false);
					}
					/* ノードMap情報のIcon選択状態セット */
					nodeMapInfo.setSelected(true);
					isLineStart = false;
				} else {
					/* Link描画モード */
					isLineStart = true;
					lineStartX = event.x;
					lineStartY = event.y;
					startNode = nodeMapInfo.getNodeInfo();
				}

				isFoundSelectedNode = true;
			} else {
				/*
				 * MouseDown位置でないノードの選択 解除
				 */
				nodeMapInfo.setSelected(false);
			}

			nodeMapNodeInfoList.set(i, nodeMapInfo);
		}

		nodMapLinkInformationList = popMenu.getNodMapLinkInformationList();

		/* 再描画 */
		mapDraw.redraw(nodeMapNodeInfoList, nodMapLinkInformationList);
	}

	/**
	 * マウスUpイベントリスナーです。 ノードIconの移動または、ノード間リンクの確定を行います。
	 * 
	 * @param event
	 *            MouseEvent
	 */
	public void mouseUp(MouseEvent event) {

		if (isLineSelected && isLineStart) {
			/*
			 * ノード間のLink描画 ノードmouseDownで既に開始点がセットされて いるので、mouseUp位置をLink終点とする
			 * 但し、既にノード間にLinkが存在する場合には Link削除とする
			 */
			isLineStart = false;
			/* 描画中Link等クリアのために描画 */
			mapDraw.redraw(nodeMapNodeInfoList, nodMapLinkInformationList);

			/*
			 * ノードMap描画におけるノード情報を順次抽出して、 Link描画/削除または、ノードIcon移動を順次 チェック・実行
			 */
			for (int i = 0; i < nodeMapNodeInfoList.size(); i++) {
				NodeMapInfo nodeMapInfo = (NodeMapInfo) nodeMapNodeInfoList
						.get(i);

				if (((event.x > nodeMapInfo.getX()) && (event.x < (nodeMapInfo
						.getX() + nodeMapInfo.getWidth())))
						&& ((event.y > nodeMapInfo.getY()) && (event.y < (nodeMapInfo
								.getY() + nodeMapInfo.getHeight())))) {
					/*
					 * mouseUpがノード位置の場合にLinkが有効
					 */
					boolean isLineCut = false;

					if (nodMapLinkInformationList == null) {
						return;
					}
					ArrayList node0List = nodMapLinkInformationList
							.getNode0address();
					ArrayList node1List = nodMapLinkInformationList
							.getNode1address();
					/*
					 * 既にノード間Linkが存在するかチェックし、存在する 場合にはLink削除
					 */
					for (int linkNodeCount = 0; linkNodeCount < node0List
							.size(); linkNodeCount++) {
						if (startNode.getIpaddress().equals(
								node0List.get(linkNodeCount))) {
							if (nodeMapInfo.getNodeInfo().getIpaddress()
									.equals(node1List.get(linkNodeCount))) {
								/* Link削除 */
								node0List.remove(linkNodeCount);
								node1List.remove(linkNodeCount);
								isLineCut = true;

								break;
							}
						}
					}
					/*
					 * 逆方向でノード間Linkが存在している かチェック
					 */
					for (int linkNodeCount = 0; linkNodeCount < node1List
							.size(); linkNodeCount++) {
						if (startNode.getIpaddress().equals(
								node1List.get(linkNodeCount))) {
							if (nodeMapInfo.getNodeInfo().getIpaddress()
									.equals(node0List.get(linkNodeCount))) {
								/* Link削除 */
								node1List.remove(linkNodeCount);
								node0List.remove(linkNodeCount);
								isLineCut = true;

								break;
							}
						}
					}

					/*
					 * 同一ノード間Linkが存在していない場合にはLink追加
					 */
					if (!isLineCut) {
						node0List.add(startNode.getIpaddress());
						node1List.add(nodeMapInfo.getNodeInfo().getIpaddress());
					}
					// ノードLink情報更新
					nodMapLinkInformationList.setNode0address(node0List);
					nodMapLinkInformationList.setNode1address(node1List);
					// redraw
					mapDraw.redraw(nodeMapNodeInfoList,
							nodMapLinkInformationList);
					break;
				}
			}
		} else {
			/*
			 * ノード間Link描画でなくノード移動処理
			 */
			isLineStart = false;

			for (int i = 0; i < nodeMapNodeInfoList.size(); i++) {
				NodeMapInfo nodeMapInfo = (NodeMapInfo) nodeMapNodeInfoList
						.get(i);

				if (nodeMapInfo.isSelected()) {
					/*
					 * 選択されているノード移動位置確定 座標セット
					 */
					int x = nodeMapInfo.getX();
					int y = nodeMapInfo.getY();

					if (x < 0) {
						x = event.x;
					}

					if (y < 0) {
						y = event.y;
					}

					nodeMapInfo.setX(x);
					nodeMapInfo.setY(y);

					NodeInformation nodeInfo = nodeMapInfo.getNodeInfo();
					String nodeX = Integer.toString(x);
					String nodeY = Integer.toString(y);
					nodeInfo.setNodeX(nodeX);
					nodeInfo.setNodeY(nodeY);
					nodeMapInfo.setNodeInfo(nodeInfo);
					nodeMapNodeInfoList.set(i, nodeMapInfo);

					/* 再描画 */
					mapDraw.redraw(nodeMapNodeInfoList,
							nodMapLinkInformationList);

					/* 選択ノード解除 */
					nodeMapInfo.setSelected(false);
					nodeMapNodeInfoList.set(i, nodeMapInfo);
				}
			}
		}

		if (NodeIcon.getInstance().isValid()) {
			NodeMapInfo addNodeMapInfo = NodeIcon.getInstance()
					.getNodeMapinfo();
			String nodeX = Integer.toString(event.x);
			String nodeY = Integer.toString(event.y);
			addNodeMapInfo.setX(event.x);
			addNodeMapInfo.setY(event.y);

			NodeInformation nodeInfo = addNodeMapInfo.getNodeInfo();
			nodeInfo.setNodeX(nodeX);
			nodeInfo.setNodeY(nodeY);
			addNodeMapInfo.setNodeInfo(nodeInfo);

			// nodeMapNodeInfoList.add(addNodeMapInfo);
			openNodeConfigEditor(nodeInfo);
			NodeIcon.getInstance().setValid(false);
			NodeIcon.getInstance().setSetViewList(false);

		}

	}

	/**
	 * マウス移動リスナーです。 モードによりノードIconの移動及びノード間リンクの描画を行います。
	 * 
	 * @param event
	 *            MouseEvent
	 */
	public void mouseMove(MouseEvent event) {

		/*
		 * ノード移動またはLink描画
		 */
		if (isLineSelected && isLineStart) {
			/*
			 * ノード間Link描画
			 */
			mapDraw.redraw(nodeMapNodeInfoList, nodMapLinkInformationList);
			gc.drawLine(lineStartX, lineStartY, event.x, event.y);
		} else {
			/*
			 * ノード選択によるノード移動
			 */
			isLineStart = false;

			for (int i = 0; i < nodeMapNodeInfoList.size(); i++) {
				/*
				 * 選択ノードをmouseカーソル位置に座標 セット
				 */
				NodeMapInfo nodeMapInfo = (NodeMapInfo) nodeMapNodeInfoList
						.get(i);

				if (nodeMapInfo.isSelected()) {
					String nodeX = Integer.toString(event.x);
					String nodeY = Integer.toString(event.y);
					nodeMapInfo.setX(event.x);
					nodeMapInfo.setY(event.y);

					NodeInformation nodeInfo = nodeMapInfo.getNodeInfo();
					nodeInfo.setNodeX(nodeX);
					nodeInfo.setNodeY(nodeY);
					nodeMapInfo.setNodeInfo(nodeInfo);
					nodeMapNodeInfoList.set(i, nodeMapInfo);

					/* 再描画 */
					mapDraw.redraw(nodeMapNodeInfoList,
							nodMapLinkInformationList);
				}
			}
		}

		if (NodeIcon.getInstance().isValid()) {
			NodeMapInfo addNodeMapInfo = NodeIcon.getInstance()
					.getNodeMapinfo();
			String nodeX = Integer.toString(event.x);
			String nodeY = Integer.toString(event.y);
			addNodeMapInfo.setX(event.x);
			addNodeMapInfo.setY(event.y);

			// mapDraw.drawNode(addNodeMapInfo);
			if (!NodeIcon.getInstance().isSetViewList()) {
				boolean isSet = false;
				for (int j = 0; j < nodeMapNodeInfoList.size(); j++) {
					NodeMapInfo checkNodeMapInfo = (NodeMapInfo) nodeMapNodeInfoList
							.get(j);
					if (checkNodeMapInfo.getNodeInfo().getIpaddress()
							.equals("")) {
						nodeMapNodeInfoList.set(j, addNodeMapInfo);
						NodeIcon.getInstance().setSetViewList(true);
						isSet = true;
					}
				}
				if(!isSet) {
					nodeMapNodeInfoList.add(addNodeMapInfo);
					NodeIcon.getInstance().setSetViewList(true);
				}
			}
			mapDraw.redraw(nodeMapNodeInfoList, nodMapLinkInformationList);
		}
	}

	/**
	 * ノードMapビューを再描画します。
	 * 
	 */
	public void redraw() {

		/* 再描画 */
		mapDraw.redraw(nodeMapNodeInfoList, nodMapLinkInformationList);
	}

	private void openNodeConfigEditor(NodeInformation nodeInfo) {


		IWorkbenchPage activePage = PlatformUI.getWorkbench()
				.getActiveWorkbenchWindow().getActivePage();

		if (nodeInfo == null) {
			nodeInfo = new NodeInformation();
			nodeInfo.setIpaddress(null);
		}

		try {
			/*
			 * Editorにブラウザをオープン
			 */
			if (NodeMapMenuNodeOperation.editorNodeConfigOperationView != null) {
				activePage.closeEditor(NodeMapMenuNodeOperation.editorNodeConfigOperationView, false);
				NodeMapMenuNodeOperation.editorNodeConfigOperationView = null;
			}

			NodeMapMenuNodeOperation.editorNodeConfigOperationView = activePage.openEditor(
					new EditorInputNodeView(EditorNodeOperation.TITLE_SUFFIX,
							nodeInfo, false), EditorNodeOperation.class
							.getName());
		} catch (PartInitException e) {

			e.printStackTrace();
		}
	}

	/**
	 * ノードMap View PopUpメニュークラスインスタンスを返します。
	 * 
	 * @return popMenu ノードMap View PopUpメニュークラスインスタンス
	 */
	public NodeMapDrawViewPopUpMenu getPopMenu() {
		return popMenu;
	}

	/**
	 * ノード間リンクライン選択状態を返します。
	 * 
	 * @return isLineSelected ノード間リンクライン選択状態
	 */
	public boolean isLineSelected() {
		return isLineSelected;
	}

	/**
	 * ノード間リンクライン開始フラグをセットします。
	 * 
	 * @param isLineSelected
	 *            ノード間リンクライン開始フラグ
	 */
	public void setLineSelected(boolean isLineSelected) {
		this.isLineSelected = isLineSelected;
	}

	/**
	 * ノード間リンクライン開始フラグを返します。
	 * 
	 * @return isLineStart ノード間リンクライン開始フラグ
	 */
	public boolean isLineStart() {
		return isLineStart;
	}

	/**
	 * ノード間リンクライン開始フラグをセットします。
	 * 
	 * @param isLineStart
	 *            ノード間リンクライン開始フラグ
	 */
	public void setLineStart(boolean isLineStart) {
		this.isLineStart = isLineStart;
	}
}
