/*
 * @(#)RidElementLink.java
 *
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 *
 * RID-X : Element
 *
 * @author  Intelligent Technology Inc.
 * @version 1.01 2001/11/07
 */
package jp.co.nri.rid.doc;

import java.awt.Color;

import java.io.File;

import java.util.Enumeration;
import java.util.Vector;

import org.w3c.dom.DOMException;

import jp.co.iti.fagot.ZApp;

import jp.co.iti.fagot.fob.FColor;
import jp.co.iti.fagot.fob.xml.XElementNS;

import jp.co.iti.fagot.geom.ZPoint;
import jp.co.iti.fagot.geom.ZPath;
import jp.co.iti.fagot.geom.ZLine;
import jp.co.iti.fagot.geom.ZRectangle;

import jp.co.iti.fagot.gob.GElement;
import jp.co.iti.fagot.gob.GLine;
import jp.co.iti.fagot.gob.GString;
import jp.co.iti.fagot.gob.GOperation;
import jp.co.iti.fagot.gob.GobManager;

import jp.co.iti.fagot.doc.DocElement;
import jp.co.iti.fagot.doc.DocDocument;
import jp.co.iti.fagot.doc.DocGridControler;
import jp.co.iti.fagot.doc.DocPane;

import jp.co.iti.fagot.util.ZInstance;
import jp.co.iti.fagot.util.ZResource;
import jp.co.iti.fagot.util.ZDebug;
import jp.co.iti.fagot.util.ZString;

import jp.co.nri.Rid;
/**
 * Rid Element(Link用)
 */
public class RidElementLink extends RidElement {

	/**
	 * デフォルトコンストラクタ
	 */
	public RidElementLink() throws DOMException {
	for ( int nIndex=0 ; nIndex<2 ; nIndex++ ) {
		mConnectContainer[nIndex] = null;
	}
	}

	/**
	 * コネクトエレメントの取得
	 * プロパティパネルで表示するための特別対応
	 * 20020926
	 */
	public String getAttribute(String name) {
	if ( name.equals("Connect0.Key" ) ) {
		return getConnectElement(0).getAttribute("Key");
	} else if ( name.equals("Connect1.Key" ) ) {
		return getConnectElement(1).getAttribute("Key");
	}
	return super.getAttribute(name);
	}

	//## 描画 #########################################################
	/**
	 * 描画エレメントの取得
	 *
	 * @param szPanelId パネル識別子
	 * @param isRoot    ルートの有無(つねにfalse)
	 */
	public Vector createGobs(String szPanelId) {
	Vector gobs = new Vector();

	// 描画オプションのチェック
	String szIntensity = getAttribute("Intensity");
	boolean bDisplay = true;
	ZResource res = ZApp.getTheApp().getResource();
	if ( szIntensity.equals("Virtual") ) {
		// Virtual
		bDisplay = res.getBoolean("view.Link.Virtual",true);
	} else {
		// Real
		bDisplay = res.getBoolean("view.Link.Real",false);
	}
	if ( !bDisplay ) {
		return gobs;
	}

/*
	*** 20020404 updateKeyAttribute() で更新するよう修正 ***

	// リンクの正当性チェック
	// リンクコンテナが、移動、削除されていた場合、
	// ドキュメント読み込み直後でリンクコンテナが設定されていない場合はここで正す
	// 性能向上のため、描画オプションのチェック↑と順番変更
	if ( ! ( checkLink(szPanelId,0) && checkLink(szPanelId,1) ) ) {
		removeFromDocTree();
		return gobs;
	}
*/

	// 画面に表示すべきリンクか否かのチェック
	boolean bZone = true;
	boolean bSelect = res.getBoolean("view.Link.Select",false);
	GElement main0 = getConnectElement(0).getMainGob(szPanelId);
	GElement main1 = getConnectElement(1).getMainGob(szPanelId);
	if ( szPanelId.equals(Rid.PANEL_ZONE) ) {
		// Zone：リンク先の双方とも描画されていないので、描画しない
		if ( main0 == null && main1 == null ) {
				bDisplay = false;
		} else if ( bSelect ) {
			if ( !( ( main0 != null && main0.isSelect() ) ||
			        ( main1 != null && main1.isSelect() ) ) ) {
				bDisplay = false;
			}
		}
	} else {
		// Map：リンク先のいずれかが描画されていないので、描画しない
		if ( main0 == null || main1 == null ) {
			bDisplay = false;

		// リンク先とリンク元が同じ場合は同一点なので描画しない
		} else if ( main0 == main1 ) {
			bDisplay = false;
		
		} else if ( bSelect ) {
			if ( !( main0.isSelect() || main1.isSelect() ) ) {
				bDisplay = false;
			}
		}
		bZone = false;
	}
	if ( !bDisplay ) {
		return gobs;
	}

	// Gobの作成
	ZPoint pt0 = getConnectPoint(szPanelId,0) ;
	ZPoint pt1 = getConnectPoint(szPanelId,1) ;

	GLine line = new GLine();

	// 色と太さの取得
	Color col = getColorAttr("PaintColor");
	if ( col == null ) {
		if ( szIntensity.equals("Virtual") ) {
			col = res.getColor("unit.color.StrongLink");
		} else {
			col = res.getColor("unit.color."+getTagName());
		}
	}
	if ( szIntensity.equals("Virtual") ) {
		line.getStyle().getStroke().getWidth().set(2.0f);
	}
	if ( !getBooleanAttr("Exist", true ) ) {
		col = res.getColor("unit.color.NotExist");
	}
	line.getStyle().setStrokeColor( new FColor( col ) );
	
	// 線と矢印
	line.setLine( new ZLine(pt0,pt1) );
	line.setArrow(GLine.ARROW_FORWARD);

	line.setContainer(this);
	if ( bZone ) {
		line.setSelect(isSelect());
		line.setGobId("zone");
	} else {
		line.setGobId("map");
		line.setSelectable( false );
	}

	// conditionの描画
	String szCond = getAttribute("Condition");
	if ( szCond.length() > 0 ) {
		ZRectangle rttxt = new ZRectangle( (pt0.getX()+pt1.getX())/2,
										   (pt0.getY()+pt1.getY())/2,
											1000,getTitleHeight());
		GString str = new GString();
		str.setText( szCond );
		str.getStyle().setFillColor( new FColor( Color.black ));
		str.setFormat( GString.SINGLE|GString.LEFT|GString.TOP );
		str.setRect( rttxt );
		line.addForeChild(str);
	}

	gobs.add( line );
	
	return gobs;
	}

	/**
	 * 描画エレメントの取得
	 *
	 * ルートの場合は、自分自身を背景として描画する
	 * @param szPanelId パネル識別子
	 * @param isRoot    ルートの有無
	 */
	public Vector createSkeltonGobs(String szPanelId,boolean isRoot) {
	return new Vector();
	}

	//## リンク先の更新 ###############################################
	/**
	 * リンク先の更新
	 * @param oldValue 昔の値（nullの場合はunitが削除された）
	 * @param unit 更新されたユニット
	 */
	public void updateKeyAttribute( String oldValue, RidElement unit ) {
	String newValue = unit.getAttribute("Key");
	String checkTarget = oldValue;
	if ( checkTarget == null ) {
		checkTarget = newValue;
	}

	// リンクターゲットの取得
	XElementNS elm0 = getChildElement("Connect0",null);
	String szKey0   = elm0.getAttribute("Key");
	XElementNS elm1 = getChildElement("Connect1",null);
	String szKey1   = elm1.getAttribute("Key");

	if ( oldValue == null ) {
		// 削除
		if ( szKey0.equals(checkTarget) || szKey1.equals(checkTarget) ) {
			removeFromDocTree();
		}
	} else {
		// 名前の変更
		if ( szKey0.equals(checkTarget) ) {
			elm0.setAttribute("Key",newValue);
			mConnectContainer[0] = unit;
		}
		if ( szKey1.equals(checkTarget) ) {
			elm1.setAttribute("Key",newValue);
			mConnectContainer[1] = unit;
		}
	}
	}

	//## IGobContainer：移動 ##########################################
	/**
	 * 要素の移動モードはなにか？
	 * @param gob リクエスト元Gob
	 * @param pt   論理座標
	 */
	public byte getPreMoveMode(GElement gob,ZPoint pt) {
	byte mode = super.getPreMoveMode(gob,pt);
	switch ( mode ) {
	  case GOperation.MOVE:		// 移動
		mode = GOperation.NONE;
		break;

	  case GOperation.NEW:		// 新規作成
	  case GOperation.POINT:	// 点の移動
		// ヒットするエレメントがあれば、オプション（接続予定マーク）を表示
		String szPanelId = ZInstance.prop().getProperty( Rid.PANEL_ACTIVE );
		if ( findConnectContainer(szPanelId,pt,null) ) {
			getConnectElement(2).setHighlight(szPanelId,mConnectPosition);
		}
	}
	return mode;
	}

	/**
	 * 移動の開始
	 * @param gob  リクエスト元Gob
	 * @param grid グリッドコントローラ
	 */
	public void moveStart(GElement gob,DocGridControler grid) {
	switch ( grid.getMoveMode() ) {
//	  case GOperation.MOVE:		// 移動
//		// 線全体の移動はしない
//		grid.setMoveMode( GOperation.NONE ) ;
//		break;

	  case GOperation.NEW:		// 新規作成
	  case GOperation.POINT:	// 点の移動
		gob.moveStartGob( grid );
		DocPane editor = grid.getEditor();
		// 新規作成の場合、開始点を設定
		if ( grid.getMoveMode() == GOperation.NEW ) {
			if ( findConnectContainer(editor.getPanelId(),grid.getStartPoint(),null) ) {
				if ( mConnectPosition != GOperation.MOVE ) {
					getConnectElement(2).setHighlight(editor.getPanelId(),mConnectPosition);
					updateConnectContainer(editor.getPanelId(),0);
				} else {
					grid.setMoveMode( GOperation.NONE ) ;
				}
			} else {
				// 開始位置のコンテナがみつからないので描画しない
				grid.setMoveMode( GOperation.NONE ) ;
				return ;
			}
		}
		break;
	}
	}

	/**
	 * 移動中Pathの取得
	 * @param gob  リクエスト元Gob
	 * @param grid グリッドコントローラ
	 */
	public ZPath getMovingPath(GElement gob,DocGridControler grid) {
//	ZDebug.trace();
	DocPane editor = grid.getEditor();
	ZPoint ptMove = grid.getMovePoint();
	ZPoint ptStop = grid.getMovePoint();	// 仮設定

	switch ( grid.getMoveMode() ) {
	  case GOperation.NEW:		// 新規作成
	  case GOperation.POINT:	// 点の移動
		// 移動しない方の座標を取得
		int nStopIndex = 0 ;
		if ( grid.getMoveMode() == GOperation.POINT ) {
			nStopIndex = (((GLine)gob).getMovePoint()==0)?1:0 ;
		}
//		ZDebug.trace("StopPoint:"+nStopIndex);
		ptStop = getConnectPoint(editor.getPanelId(),nStopIndex);

		// 移動点に接続コンテナがある場合は、接続コンテナから移動点を取得
		if ( findConnectContainer(editor.getPanelId(),grid.getMovePoint(),getConnectElement(nStopIndex)) ) {
			getConnectElement(2).setHighlight(editor.getPanelId(),mConnectPosition);
			if ( mConnectPosition != GOperation.MOVE ) {
				ptMove = getConnectPoint(editor.getPanelId(),2);
			}
		}
		break;
	}

	return new ZPath( new ZLine( ptMove,ptStop ) );
	}

	/**
	 * 移動の完了
	 * @param gob  リクエスト元Gob
	 * @param grid グリッドコントローラ
	 */
	public void moveEnd(GElement gob,DocGridControler grid) {
	DocPane editor = grid.getEditor();

	switch ( grid.getMoveMode() ) {
	  case GOperation.NEW:		// 新規作成
		if ( findConnectContainer(editor.getPanelId(),grid.getMovePoint(),getConnectElement(0)) ) {
			if ( mConnectPosition != GOperation.MOVE ) {
				updateConnectContainer(editor.getPanelId(),1);
				// 追加
				setAttribute("Intensity","Virtual");
				setAttribute("Creator","Operator");
				setSelect(gob,true);
				DocDocument doc = editor.getDocument();
				setDocument( doc );
				DocElement link = (DocElement)((DocElement)doc.getDocumentElement()).getChildElement("Link",null);
				link.appendChild(this);
			}
		}
		break;

	  case GOperation.POINT:	// 点の移動
		// 移動しない方の座標を取得
		int nStopIndex = (((GLine)gob).getMovePoint()==0)?1:0 ;
		if ( findConnectContainer(editor.getPanelId(),grid.getMovePoint(),getConnectElement(nStopIndex)) ) {
			if ( mConnectPosition != GOperation.MOVE ) {
				updateConnectContainer(editor.getPanelId(),((GLine)gob).getMovePoint());
			}
		}
		break;
	}

	}

	//## リンク情報 ###################################################
	/**
	 * 接続できるコンテナを探す。見つかった場合、接続リストの2番目(一時領域)に保存する
	 * @param szPanelId パネル識別子
	 * @param pt        座標
	 * @param pair      接続元となるエレメント
	 */
	protected boolean findConnectContainer(String szPanelId, ZPoint pt,RidElement pair) {
	mConnectContainer[2] = null;
	mConnectPosition     = 0;
	Enumeration elm = GobManager.elements(szPanelId,GobManager.ALL);
	while ( elm.hasMoreElements() ) {
		GElement item = (GElement)elm.nextElement();
		byte pos = item.getConnectMode(pt, this);
		if ( pos != 0 ) {
			RidElement container = (RidElement)item.getContainer();
			if ( container != null && container != pair ) {
//				ZDebug.trace("find:"+pos);
				mConnectContainer[2] = container;
				mConnectPosition     = pos;
				return true;
			}
		}
	}
	return false;
	}

	/**
	 * 検出済の接続できるコンテナをDOMに反映する
	 * @param szPanelId パネル識別子
	 * @param nIndex 接続位置
	 */
	protected void updateConnectContainer(String szPanelId, int nIndex) {
	// オブジェクトの設定
	// undo時にクリアされないので、nullに設定する
	mConnectContainer[nIndex] = mConnectContainer[2] ;

	// DOMの設定
	XElementNS elm = getChildElement("Connect"+nIndex,null);
	String szKey = mConnectContainer[2].getAttribute("Key");
	elm.setAttribute("Key",szKey);
	elm.setByteAttr("Position",mConnectPosition);

	mConnectContainer[2] = null;
	}

	/**
	 * リンク情報の取得
	 * @param szPanelId パネル識別子
	 * @param nIndex 位置キー
	 */
	protected ZPoint getConnectPoint(String szPanelId,int nIndex) {
	// 位置情報の取得
	byte connectPosition = mConnectPosition;
	if ( nIndex != 2 ) {
		XElementNS elm = findChildElement(".Connect"+nIndex,null);
		if ( nIndex == 0 ) {
			connectPosition = elm.getByteAttr("Position",GOperation.ZOOMSW);
		} else if ( nIndex == 1 ) {
			connectPosition = elm.getByteAttr("Position",GOperation.ZOOMNE);
		}
	}

	return getConnectElement(nIndex).getConnectPoint(szPanelId, connectPosition);
	}

	/**
	 * リンク対象の取得
	 * @param nIndex 位置キー
	 */
	protected RidElement getConnectElement(int nIndex) {
	if ( nIndex != 2 && mConnectContainer[nIndex] == null ) {
		XElementNS elm = getChildElement("Connect"+nIndex,null);
		String szKey   = elm.getAttribute("Key");

		// ポインタがセットされていない場合は、Topから検索
		RidDocumentSite doc = (RidDocumentSite)getOwnerDocument();
		RidElementZone root= doc.getTopZone();
		mConnectContainer[nIndex] = (RidElement)root.findChildElement(".UrlUnit","Name="+szKey);
		if ( mConnectContainer[nIndex] == null ) {
			mConnectContainer[nIndex] = doc.findChildElementInZone("PageUnit",szKey);
		}
		if ( mConnectContainer[nIndex] == null ) {
			ZDebug.err("Connect element find failed:"+szKey);
		}
	}
	return mConnectContainer[nIndex];
	}

	//## リンク情報 ###################################################
	/**
	 * リンクペアの取得
	 * @param from 接続元となるエレメント
	 */
	protected RidElement getLinkToElement(RidElement from) {
	RidElement linkTo = null;

	if ( getConnectElement(0) == from ) {
		linkTo = getConnectElement(1);
	}

	if ( linkTo != null && linkTo.getStatus().equals("remove") ) {
		ZDebug.trace("no link");
		linkTo = null;
	}

	return linkTo;
	}

	/**
	 * リンクペアの取得
	 * @param to 接続先となるエレメント
	 */
	protected RidElement getLinkFromElement(RidElement to) {
	RidElement linkFrom = null;

	if ( getConnectElement(1) == to ) {
		linkFrom = getConnectElement(0);
	}

	if ( linkFrom != null && linkFrom.getStatus().equals("remove") ) {
		ZDebug.trace("no link");
		linkFrom = null;
	}

	return linkFrom;
	}

	//## アトリビュート################################################
	/**
	 * 接続エレメント
	 * 接続するのは２点だが、3つ目の領域を編集用一時領域として利用する
	 */
	protected RidElement[] mConnectContainer = new RidElement[3] ;

	/**
	 * 編集用一時領域(3つ目)の接続位置
	 */
	protected byte mConnectPosition = 0 ;
}
