/*
 * @(#)GElement.java
 *
 * Copyright 2001 by Intelligent Technology Inc. All rights reserved.
 */
package jp.co.iti.fagot.gob;

import java.awt.Rectangle;
import java.awt.Graphics2D;

import java.awt.geom.Rectangle2D;

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

import javax.swing.JComponent;

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

import jp.co.iti.fagot.fob.FobManager;
import jp.co.iti.fagot.fob.IFobContainer;

import jp.co.iti.fagot.doc.DocManager;
import jp.co.iti.fagot.doc.DocGraphics;
import jp.co.iti.fagot.doc.DocGridControler;

import jp.co.iti.fagot.util.ZDebug;
/**
 * 描画構造上の部品を記述します。
 *
 * 【仕様】
 * 以下の設計方針を元にクラスを定義する
 * <ul>
 * <li>拡大、縮小をjava.awt.Graphicsで行うとStrokeの太さまで変化してしまう
 *     ので座標を自己管理
 * <li>拡大、縮小、移動、回転の繰り返しによる誤差の最小化
 * </ul>
 *
 * @author  福島康治
 * @version 1.01 2001/01/07
 */
public class GElement implements IFobContainer {

	/**
	 * コンストラクタ
	 */
	public GElement() {
		//{{FAGOT_ATTR
		mStyle     = new PStyle();
		mTransform = new PTransform();
		//}}FAGOT_ATTR
	}

	//## コンテナ #####################################################
	/**
	 * コンテナの設定
	 * @param container コンテナ
	 */
	public void setContainer(IGobContainer container) {
	mContainer = container;
	}

	/**
	 * コンテナの取得
	 */
	public IGobContainer getContainer() {
	return mContainer;
	}

	/**
	 * コンテナの削除
	 */
	public void removeContainer() {
	if ( mContainer != null ) {
		mContainer.removeGob(this);
	}
	}

	/**
	 * 識別子の設定
	 * コンテナ側からGElementを識別するためのIDを設定できる
	 * この値は、コンテナ用の値であるので、undo不要
	 * @param szId 識別子
	 */
	public void setGobId(String szId) {
	mGobId = szId;
	}

	/**
	 * 識別子の取得
	 */
	public String getGobId() {
	return mGobId;
	}

	/**
	 * 選択できない前面描画オブジェクトの追加
	 * @param children 子供
	 */
	public void addForeChildren(Vector children) {
	mForeChildren.addAll(children);
	}

	/**
	 * 選択できない前面描画オブジェクトの追加
	 * @param child 子供
	 */
	public void addForeChild(GElement child) {
	mForeChildren.add(child);
	}

	/**
	 * 選択できない前面描画オブジェクトの取得
	 * @return 子供
	 */
	public Vector getForeChildren() {
	return mForeChildren;
	}

	/**
	 * 選択できない背面描画オブジェクトの追加
	 * @param children 子供
	 */
	public void addBackChildren(Vector children) {
	mBackChildren.addAll(children);
	}

	/**
	 * 選択できない背面描画オブジェクトの追加
	 * @param child 子供
	 */
	public void addBackChild(GElement child) {
	mBackChildren.add(child);
	}

	/**
	 * 選択できない背面描画オブジェクトの取得
	 * @return 子供
	 */
	public Vector getBackChildren() {
	return mBackChildren;
	}

	//## 操作 #########################################################
	/**
	 * 選択の取得
	 */
	public boolean isSelect() {
	return mSelect;
	}

	/**
	 * 選択の設定
	 * @param bSel 選択状態
	 */
	public boolean setSelect(boolean bSel) {
	if ( mSelectable ) {
		if ( mContainer != null ) {
			mSelect = mContainer.setSelect(this,bSel);
		} else {
			mSelect = bSel;
		}
	}
	return mSelect;
	}

	/**
	 * 選択可不可の設定
	 * @param bSel 選択可不可状態
	 */
	public void setSelectable(boolean bSel) {
	mSelectable = bSel;
	}

	//## Container #####################################################
	/**
	 * 要素にヒットするか？
	 * @param pt   論理座標
	 */
	public byte getHitMode(ZPoint pt) {
	if ( mSelectable ) {
		ZPoint ptTrans = getTransform().inverseTransform(pt);

		if ( mContainer != null ) {
			// コンテナに問い合わせ
			return mContainer.getHitMode(this,ptTrans);
		}
		return getHitModeGob(ptTrans);
	}
	return GOperation.NONE;
	}

	/**
	 * 要素にヒットするか？(コンテナが存在しない場合デフォルトメソッド)
	 * @param pt 論理座標
	 */
	public byte getHitModeGob(ZPoint pt) {
		return GOperation.NONE;
	}

	/**
	 * 要素の移動モードはなにか？
	 * @param pt   論理座標
	 */
	public byte getPreMoveMode(ZPoint pt) {
	if ( mSelectable ) {
		ZPoint ptTrans = getTransform().inverseTransform(pt);

		if ( mContainer != null ) {
			// コンテナに問い合わせ
			return mContainer.getPreMoveMode(this,ptTrans);
		}
		return getPreMoveModeGob(ptTrans);
	}
	return GOperation.NONE;
	}

	/**
	 * 要素の移動モードはなにか？(コンテナが存在しない場合デフォルトメソッド)
	 * @param pt 論理座標
	 */
	public byte getPreMoveModeGob(ZPoint pt) {
		return getHitModeGob(pt);
	}

	/**
	 * 要素に接続するか？
	 * @param pt   論理座標
	 * @param obj  接続対象
	 */
	public byte getConnectMode(ZPoint pt, Object obj) {
	if ( mSelectable ) {
		ZPoint ptTrans = getTransform().inverseTransform(pt);

		if ( mContainer != null ) {
			// コンテナに問い合わせ
			return mContainer.getConnectMode(this,ptTrans,obj);
		}
		return getConnectModeGob(ptTrans, obj);
	}
	return GOperation.NONE;
	}

	/**
	 * 要素に接続するか？(コンテナが存在しない場合デフォルトメソッド)
	 * @param pt   論理座標
	 * @param obj  接続対象
	 */
	public byte getConnectModeGob(ZPoint pt, Object obj) {
		return GOperation.NONE;
	}

	/**
	 * 要素の接続位置取得
	 * @param mode 接続位置
	 */
	public ZPoint getConnectPointGob(byte mode) {
	return new ZPoint(0,0);
	}

	/**
	 * ダブルクリックの動作
	 * @param mode クリック位置
	 */
	public void verbDblClick(ZPoint pt) {
	ZPoint ptTrans = getTransform().inverseTransform(pt);

	if ( mContainer != null ) {
		mContainer.verbDblClick(this,ptTrans);
	} else {
		verbDblClickGob(ptTrans);
	}
	}

	/**
	 * ダブルクリックの動作
	 * @param mode クリック位置
	 */
	public void verbDblClickGob(ZPoint pt) {
	}

	//## 移動 #########################################################
	/**
	 * 移動の開始
	 * @param grid グリッドコントローラ
	 */
	public void moveStart(DocGridControler grid) {
	// 座標変換の適用
	grid.pushTransform(getTransform());

	if ( mContainer != null ) {
		mContainer.moveStart(this,grid);
	} else {
		moveStartGob(grid);
	}

	// 座標変換を元に戻す
	grid.popTransform();
	}

	/**
	 * 移動の開始(コンテナが存在しない場合デフォルトメソッド)
	 * @param grid グリッドコントローラ
	 */
	public void moveStartGob(DocGridControler grid) {
	}

	/**
	 * 移動中Pathの取得
	 * @param grid グリッドコントローラー
	 */
	public ZPath getMovingPath(DocGridControler grid) {
	if ( mContainer != null ) {
//		ZDebug.trace();
		return mContainer.getMovingPath(this,grid);
	}
	return getMovingPathGob(grid);
	}

	/**
	 * 移動中Pathの取得(コンテナが存在しない場合デフォルトメソッド)
	 * @param grid グリッドコントローラ
	 */
	public ZPath getMovingPathGob(DocGridControler grid) {
	return null;
	}


	/**
	 * 移動の完了
	 * @param grid グリッドコントローラー
	 */
	public void moveEnd(DocGridControler grid) {
	// 座標変換の適用
	grid.pushTransform(getTransform());

	if ( mContainer != null ) {
		mContainer.moveEnd(this,grid);
	} else {
		moveEndGob(grid);
	}

	// 座標変換を元に戻す
	grid.popTransform();
	}

	/**
	 * 移動の完了(コンテナが存在しない場合デフォルトメソッド)
	 * @param grid グリッドコントローラ
	 */
	public void moveEndGob(DocGridControler grid) {
	}

	/**
	 * 移動の完了が必要か？
	 */
	public boolean needsMoveEnd() {
	return false;
	}

	//## 位置 #########################################################
	/**
	 * 子供も含めた矩形の取得
	 */
	public ZRectangle getBounds() {
	return getMyBounds();
/*	Vector children = new Vector();
	children.addAll(mBackChildren);
	children.addAll(mForeChildren);
	ZRectangle rect = GobManager.getTransformedBounds(children);
	ZRectangle myRect = getMyBounds();
	if ( myRect != null ) {
		if ( rect == null ) {
			rect = myRect;
		} else {
			rect.union(myRect);
		}
	}
	return rect;
*/
	}

	/**
	 * 自分自身の矩形の取得
	 */
	public ZRectangle getMyBounds() {
	return null;
	}

	/**
	 * 座標変換済の矩形の取得
	 */
	public ZRectangle getTransformedBounds() {
	return new ZRectangle(getTransform().transform(getBounds()).getBounds2D());
	}

	/**
	 * 描画スタイルの設定
	 */
	public void setStyle( PStyle style ) {
	mStyle.set( style );
	}

	/**
	 * 描画スタイルの取得
	 */
	public PStyle getStyle() {
		return mStyle;
	}

	/**
	 * 座標変換の設定
	 */
	public void setTransform( PTransform trans ) {
	mTransform.set( trans );
	}

	/**
	 * 座標変換の追加
	 */
	public void addTransform( PTransform trans ) {
//	ZDebug.trace("!!add my:"+getTransform());
//	ZDebug.trace("     add:"+trans);
	mTransform.add( trans );
//	ZDebug.trace("     res:"+getTransform());
	}

	/**
	 * 座標変換
	 */
	public PTransform getTransform() {
		return mTransform;
	}

	//## 描画 #########################################################
	/**
	 * 描画
	 */
	public void paint(DocGraphics graphics) {
	// 座標変換の適用
	graphics.getGridControler().pushTransform(getTransform());
//	ZDebug.trace("---:"+getTransform());

	if ( paintCheck( graphics ) ) {
		paintBackChildren(graphics);
		paintContents(graphics);
		paintForeChildren(graphics);
	}

	// 座標変換を元に戻す
	graphics.getGridControler().popTransform();
	}

	/**
	 * 要素固有の描画
	 */
	public void paintContents(DocGraphics graphics) {
	}

	/**
	 * 前面子要素の描画
	 */
	public void paintForeChildren(DocGraphics graphics) {
	Enumeration elm = mForeChildren.elements();
	while( elm.hasMoreElements() ) {
		GElement item = (GElement)elm.nextElement();
		item.paintContents(graphics);
	}
	}

	/**
	 * 背面子要素の描画
	 */
	public void paintBackChildren(DocGraphics graphics) {
	Enumeration elm = mBackChildren.elements();
	while( elm.hasMoreElements() ) {
		GElement item = (GElement)elm.nextElement();
		item.paintContents(graphics);
	}
	}

	/**
	 * 性能向上のため描画を間引くか否かを判定する。
	 * main gobでのみ判定し、子gobはmain gobに従う
	 * @param graphics グラフィックス
	 */
	protected boolean paintCheck(DocGraphics graphics) {
	return true;
	}

	//## 描画 #########################################################
	/**
	 * 選択点の描画
	 */
	public void paintSelector(DocGraphics graphics) {
	}

	/**
	 * 移動中の描画
	 */
	public void paintDrag(DocGraphics graphics) {
	// 座標変換の適用
	graphics.getGridControler().pushTransform(getTransform());
	paintDragContents(graphics);

	// 座標変換を元に戻す
	graphics.getGridControler().popTransform();
	}

	/**
	 * 要素固有の移動中の描画
	 */
	public void paintDragContents(DocGraphics graphics) {
	}

	//## FAGOT ########################################################
	/**
	 * Fobマネージャの登録
	 * @param mgr マネージャ
	 */
	public void setFobManager(FobManager mgr) {
		//{{FAGOT_ATTR
		mStyle.setFobManager(mgr,this);
//		mTransform.setFobManager(mgr,this);
		//}}FAGOT_ATTR
	}

	//## CLONE ########################################################
	/**
	 * 複製の作成（マネージャは複製しない）
	 */
	public Object clone() {
	Object obj = createClone();
	copyAttribute( obj );
	return obj;
	}

	/**
	 * 複製インスタンスの作成
	 * 各実現クラスにおいてインスタンスを生成する
	 * 仮想クラス（およびインスタンス化されないクラス）ではnullを返す
	 */
	public Object createClone() {
	return new GElement();
	}

	/**
	 * アトリビュートの複製
	 * @param obj 複製先のオブジェクト
	 */
	public void copyAttribute( Object obj ) {
	((GElement)obj).setStyle( (PStyle)mStyle.clone() );
	((GElement)obj).setTransform( (PTransform)mTransform.clone() );
	}

	//## アトリビュート################################################
	/**
	 * コンテナ
	 * DOM Tree上の描画ではなく、GOB Tree上の描画であれば、コンテナは存在しない
	 */
	protected IGobContainer	mContainer = null;

	/**
	 * 識別子
	 */
	protected String		mGobId = null;

	/**
	 * 前面子供
	 */
	protected Vector		mForeChildren = new Vector();

	/**
	 * 背面子供
	 */
	protected Vector		mBackChildren = new Vector();

	//## アトリビュート:FAGOT##########################################
	/**
	 * 描画スタイル
	 */
	protected PStyle		mStyle;

	/**
	 * 座標変換
	 */
	protected PTransform	mTransform;

	/**
	 * 選択有無
	 */
	protected boolean  mSelect     = false;

	/**
	 * 選択可不可
	 */
	protected boolean  mSelectable = true;

	/**
	 * ハイライトの有無
	 */
//	protected boolean mHighlight  = false;

}
