/**
 * @(#)DocUIAdapter.java
 *
 * Copyright 2000 by Intelligent Technology Inc. All rights reserved.
 *
 * UI動作の捕捉
 */
package jp.co.iti.fagot.doc;

import java.awt.Cursor;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Window;

import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;

import java.util.Enumeration;
import java.util.Vector;
//import java.util.EventListener;

import javax.swing.JComponent;
import javax.swing.JViewport;
import javax.swing.JPopupMenu;

import jp.co.iti.fagot.ZApp;

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

import jp.co.iti.fagot.gob.GElement;
import jp.co.iti.fagot.gob.GOperation;
import jp.co.iti.fagot.gob.GOperationCreate;
import jp.co.iti.fagot.gob.GOperationMove;
import jp.co.iti.fagot.gob.GOperationSelect;
import jp.co.iti.fagot.gob.GOperationZoom;
import jp.co.iti.fagot.gob.GobManager;

import jp.co.iti.fagot.ui.UIFactory;

import jp.co.iti.fagot.util.ZUIUtil;
import jp.co.iti.fagot.util.ZInstance;
import jp.co.iti.fagot.util.ZDebug;
import jp.co.iti.fagot.util.ZReverseEnumeration;
/**
 * UI動作を捕捉します。
 * <ul>
 * <li>マウスイベント
 * <li>マウスモーションイベント
 * <li>キーボードイベント
 * </ul>
 *
 * コンテナごとにマウスアダプタを書くのは非常に大変なので、
 * 差分はView,Rootなどで吸収する
 *
 * @author  福島康治
 * @version 1.01 2000/12/01
 */
public class DocUIAdapter implements MouseListener,MouseMotionListener,KeyListener{
//	   implements EventListener,MouseListener,MouseMotionListener,KeyListener{

	/**
	 * コンストラクタ
	 * @param container コンテナ
	 * @param root		ルートエレメント
	 */
	public DocUIAdapter(DocPane container){
	mEditor = container;
	}

	//## イベント #####################################################
	/**
	 * 左ボタンdown
	 * @param evt マウスイベント
	 */
	protected void OnLButtonDown( MouseEvent evt ) {
//	ZDebug.trace("左ボタン開始");
	mMoveMode = GOperation.NONE;

	// 編集モードの終了
	// 編集中だった場合は、編集キャンセルのみ
	if ( mEditor.editEnd() ) {
		return ;
	}

	mStartPoint = new ZPoint(evt.getPoint());
//	ZDebug.trace("OnLButtonDown.mStartpt="+mStartPoint.toString());

	// 新規作成、移動、拡大の場合はオペレーションの作成
	GElement ope = createOperation(evt);
	if ( ope != null ) {
		mMoveMode = ope.getHitMode(mStartPoint);
		GobManager.setSelect(mEditor.getPanelId(),false);
		GobManager.setCurrent(mEditor.getPanelId(),(GElement)ope);
	}

	// 現在選択中要素にヒットするか？
	if ( mMoveMode == GOperation.NONE ) {
//		mMoveMode = GobManager.getSelectedHitMode(mEditor,mStartPoint);
		mMoveMode = GobManager.getSelectedPreMoveMode(mEditor,mStartPoint);

		// 現在選択中要素にヒットしたか？
		if ( mMoveMode != GOperation.NONE ) {
/*
			// 現在選択要素の上にもさらにエレメントが存在するか？
			GElement elm = GobManager.getHitElement(mEditor,mStartPoint);
			if ( !elm.isSelect() ) {
				// 現在選択エレメント以外のエレメントがヒットしたので、選択モードをNONEに設定
//				ZDebug.trace("SetSelect:false");
				mMoveMode = GOperation.NONE;
				GobManager.setSelect(mEditor.getPanelId(),false);
			} else {
				// 現在選択エレメントにヒットしたので、選択されているエレメントをカレントに設定
				GobManager.updateCurrent(mEditor.getPanelId());
			}
*/
				// 現在選択エレメントにヒットしたので、選択されているエレメントをカレントに設定
				GobManager.updateCurrent(mEditor.getPanelId());
		}
	}

	// 現在選択中要素にヒットしない時、ヒットオブジェクトを検索
	if ( mMoveMode == GOperation.NONE ) {
		GElement elm = GobManager.getHitElement(mEditor,mStartPoint);

		// シフトキーがおされていなければ一旦全解除
		if ( !evt.isShiftDown() ) {
			GobManager.setSelect(mEditor.getPanelId(),false);
		}
		//  なにかにヒットした
		if ( elm != null ) {
//			elm.setSelect(true);
			GobManager.updateCurrent(mEditor.getPanelId());
			GobManager.addCurrent(mEditor.getPanelId(),elm);
			mMoveMode = GOperation.MOVE;
		} else {
			GobManager.setCurrent(mEditor.getPanelId(),new GOperationSelect());
			mMoveMode = GOperation.SELECTION;
		}
	}

	// トランザクション開始
	mEditor.getDocument().beginTransaction();

	// 移動開始
	mEditor.getGridControler().setMoveStart( mStartPoint,mMoveMode ) ;
	GobManager.moveStart(mEditor,mEditor.getGridControler());

	// 移動中
	mEditor.getGridControler().setMoving( mStartPoint,0 ) ;

	// カーソルの変更と再描画
	mEditor.setCursor(GOperation.getCursor(mMoveMode));
//	ZDebug.trace();
	// すぐボタンを離すかもしれないので、遅延描画する
	// 1秒たってもマウスが動かされなければ再描画される
	// その前にマウスが動かされたなら、mouseMoveから呼ばれるrepaintで再描画される
	mEditor.repaint(1000);
	}

	/**
	 * 左ボタンup
	 * @param evt マウスイベント
	 */
	protected void OnLButtonUp( MouseEvent evt ) {

//	ZDebug.trace("OnLButtonUp");
	ZPoint endPoint = new ZPoint( evt.getPoint() ) ;
	mEditor.getGridControler().setMoving( endPoint,0 ) ;

	// 終了
	Vector cur = GobManager.getCurrent(mEditor.getPanelId());
	if ( cur.size() == 0 ) {
		// 編集中だった場合は、カレントがnullになっています
		// カレントがnullなのでコミットしない
		return ;
	}

	// 移動しなかった場合
	if ( mStartPoint.equals( endPoint ) ) {
		if ( cur.size() == 1 ) {
			GElement item = (GElement)cur.elementAt(0);
			// 新規作成オペレーション、拡大縮小オペレーションのみ座標が変化しなくても
			// MoveEndが必要
			if ( item.needsMoveEnd() ) {
				GobManager.moveEnd(mEditor,mEditor.getGridControler());
			}
			item.setSelect( true );

		// 選択の変更
		} else {
			GElement item = GobManager.getCurrentHitElement(mEditor,endPoint);
			if ( item != null ) {
				// シフトキーが押されていた場合は、ON/OFFトグル
				if ( evt.isShiftDown() ) {
					item.setSelect( !item.isSelect() );
				} else {
					item.setSelect( true );
				}
			}
		}

		// プロパティパネルの更新
		jp.co.nri.rid.RUpdateManager.update(jp.co.nri.rid.RUpdateManager.SELECT_GOBS);

	// 移動した場合
	} else {
		GobManager.moveEnd(mEditor,mEditor.getGridControler());
		GobManager.setCurrentSelect(mEditor.getPanelId(),true);
	}

	GobManager.setCurrent(mEditor.getPanelId(),null);
	mEditor.updateGobs();

	mEditor.getDocument().commit();
	updateCursor(evt);
//	ZDebug.trace();
	mEditor.repaint();
//	ZDebug.trace("左ボタン終了");
	}

	/**
	 * 右ボタンdown
	 * @param evt マウスイベント
	 */
	protected void OnRButtonDown( MouseEvent evt ) {

//	System.out.println("OnRButtonDown");
	// 編集モードの終了
	// 編集中だった場合は、編集キャンセルのみ
	if ( mEditor.editEnd() ) {
		return ;
	}

	// 現在オブジェクトがある場合は、操作中なので操作のキャンセル
	Vector cur = GobManager.getCurrent(mEditor.getPanelId());
	if ( cur.size() != 0 ) {
		mEditor.getDocument().rollback();
		GobManager.setCurrent(mEditor.getPanelId(),null);
		mEditor.setCursor(new Cursor(Cursor.DEFAULT_CURSOR) );
//		mEditor.repaint();

	// 現在オブジェクトがない場合は、右ボタンメニュー
	} else {
		// クリック位置の取得
		ZPoint pt = new ZPoint(evt.getPoint());

		// 現在選択中要素にヒットするか？
		if ( GobManager.getSelectedHitMode(mEditor,pt) == GOperation.NONE ) {
			// ヒットしない場合は、選択を全解除してヒットエレメントの検出
//			ZDebug.trace("SetSelect:false");
			GobManager.setSelect(mEditor.getPanelId(),false);
			GElement elm = GobManager.getHitElement(mEditor,pt);
			if ( elm != null ) {
//				ZDebug.trace("SetSelect:true");
				elm.setSelect(true);
			}
		}

		// メニュー共通キーの作成
		Vector gobs = GobManager.getSelectedElement(mEditor.getPanelId());
		Vector targets = new Vector();
//		ZDebug.trace("SetElement.count:"+gobs.size());

/*
		if ( true ) {
			// リスト系右ボタンメニューのテスト
			Vector targets = new Vector();
			Enumeration ge = gobs.elements();
			while ( ge.hasMoreElements() ) {
				DocElement target = (DocElement)((GElement)ge.nextElement()).getContainer();
				targets.add( target );
			}
			jp.co.nri.rid.RMenu.createContextMenu( evt, targets );
			return;
		}
*/

		// 選択されたエレメントがなければRootElement
		UIFactory ui = DocManager.createUIFactory(DocManager.ELEMENT);
		String szContext = null;
		if ( gobs.size() == 0 ) {
			szContext = mEditor.getPanelId();
			ui = DocManager.createUIFactory(DocManager.DOCUMENT);
		} else if ( gobs.size() == 1 ) {
			DocElement target = (DocElement)((GElement)gobs.firstElement()).getContainer();
			targets.add( target );
			szContext = target.getContextKey(mEditor.getPanelId());
		}

		JPopupMenu popup = null;
		if ( szContext != null ) {
//			ZDebug.trace();
			popup = ui.createContextMenu(mEditor,szContext);
		} else {
//			ZDebug.trace();
			Vector keys = new Vector();
			Enumeration elm = gobs.elements();
			while(elm.hasMoreElements()) {
//				ZDebug.trace();
				DocElement target = (DocElement)((GElement)elm.nextElement()).getContainer();
				keys.add( target.getContextKey(mEditor.getPanelId()) );
				targets.add( target );
			}
			popup = ui.createContextMenu(mEditor,keys);
		}

		// 表示位置の計算
		Dimension scrDim = popup.getToolkit().getScreenSize();
		Dimension popDim = popup.getPreferredSize();
		Window win = ZUIUtil.getParentWindow(mEditor);
		Point  ptEditor = mEditor.getLocationOnScreen();
		Point  ptWin = win.getLocationOnScreen();

//		ZDebug.trace("popDim:"+popDim);
//		ZDebug.trace("scrDim:"+scrDim);
//		ZDebug.trace("ptEditor:"+ptEditor);

		// ウィンドウ上の位置に修正
		int nX = (int)(evt.getX()+ptEditor.getX()-ptWin.getX());
		int nY = (int)(evt.getY()+ptEditor.getY()-ptWin.getY());
		// 位置の補正
		if ( nX+ptWin.getX()+popDim.getWidth() > (scrDim.getWidth()-40) ) {
			nX -= popDim.getWidth();
		}
		if ( nY+ptWin.getY()+popDim.getHeight() > (scrDim.getHeight()-40) ) {
			nY -= popDim.getHeight();
		}

		ZInstance.map().put("doc.rmenu.target", targets );
		popup.show(win, nX, nY);

//		popup.show(mEditor, evt.getX(),evt.getY());
	}
//	ZDebug.trace();
	mEditor.repaint();
	}

	/**
	 * 右ボタンup
	 */
	protected void OnRButtonUp( MouseEvent evt ) {
//	System.out.println("OnRButtonUp");
	}

	/**
	 * ダブルクリック
	 */
	protected void OnDblClick( MouseEvent evt ) {
//	ZDebug.trace();
	ZPoint pt = new ZPoint(evt.getPoint());
	// 現在選択中要素にヒットするか？
	String panelId = mEditor.getPanelId();
	ZPoint transPt = ((DocElement)mEditor.getDocument().getRootElement(panelId)).getDisplayScaledTransform(panelId).inverseTransform(pt);
	
	// 現在選択されているエレメントの取得
	Enumeration elm = new ZReverseEnumeration(GobManager.getSelectedElement( panelId ));
	while ( elm.hasMoreElements() ) {
//		ZDebug.trace();
		GElement item = (GElement)elm.nextElement();
		byte mode = item.getHitMode(transPt);
		if ( mode != GOperation.NONE ) {
			item.verbDblClick( transPt );
			break;
		}
	}
	}

	//## KeyListener ##################################################
	/**
	 * キーボードが押された
	 */
	public void keyPressed(KeyEvent e) {
	if ( e.getKeyCode() == KeyEvent.VK_SPACE ) {
//		ZDebug.trace("space in");
		ZInstance.prop().putBoolean(KEY_VK_SPACE,true);
	}
	}

	/**
	 * キーボードが押された
	 */
	public void keyReleased(KeyEvent e) {
	if ( e.getKeyCode() == KeyEvent.VK_SPACE ) {
//		ZDebug.trace("space out");
		ZInstance.prop().putBoolean(KEY_VK_SPACE,false);
	}
//	ZDebug.trace("KeyRelease:"+e);
	}

	/**
	 * キーボードが押された
	 */
	public void keyTyped(KeyEvent e) {
	}

	//## MouseListener ################################################
	/**
	 * マウスがクリックされた
	 */
	public void mouseClicked(MouseEvent evt) {
	if(evt.getClickCount() == 2) {
		OnDblClick( evt );
	}
	}

	/**
	 * マウスが押された
	 */
	public void mousePressed(MouseEvent evt) {
	if (  ( evt.getModifiers() & InputEvent.BUTTON3_MASK ) != 0 ) {
		OnRButtonDown( evt );
	} else if (  ( evt.getModifiers() & InputEvent.BUTTON1_MASK ) != 0 ) {
		OnLButtonDown( evt );
	}
	}

	/**
	 * マウスが離された
	 */
	public void mouseReleased(MouseEvent evt) {
	if (  ( evt.getModifiers() & InputEvent.BUTTON3_MASK ) != 0 ) {
		OnRButtonUp( evt );
	} else if (  ( evt.getModifiers() & InputEvent.BUTTON1_MASK ) != 0 ) {
		OnLButtonUp( evt );
	}
	}

	/**
	 * マウスが入った
	 */
	public void mouseEntered(MouseEvent evt) {
	}

	/**
	 * マウスが出た
	 */
	public void mouseExited(MouseEvent evt) {
	}

	//## MouseMotionListener ##########################################
	/**
	 * マウスがドラッグされながら移動中
	 * @param evt マウスイベント
	 */
	public void mouseDragged(MouseEvent evt){

	// ビューポートの取得
	JViewport port  = (JViewport)mEditor.getParent();

	Point  ptCur  = evt.getPoint();
	Point  ptTL   = port.getViewPosition();

	Dimension dim = port.getSize();
	Dimension dc  = mEditor.getSize();

	// 手づかみの場合はここで移動する
	if ( mMoveMode == GOperation.HANDON ) {
		ZPoint ptSta = mStartPoint ;

		Point pt = new Point( 
			(int)(ptSta.getX()-(ptCur.getX()-ptTL.x)),
			(int)(ptSta.getY()-(ptCur.getY()-ptTL.y)) );
		
		if (     pt.x < 0 )                    { pt.x = 0 ; }
		else if( pt.x > (dc.width-dim.width) ) { pt.x=dc.width-dim.width ;}
		if (     pt.y < 0 )                    { pt.y = 0 ; }
		else if( pt.y > (dc.height-dim.height)){ pt.y=dc.height-dim.height;}

		if ( pt.x != ptTL.x || pt.y != ptTL.y ) {
			port.setViewPosition( pt );
//			ZDebug.trace();
			mEditor.repaint();
		}
		return;
	}

	int offX = 0;
	int offY = 0;
	// 画面からはみ出ていた場合は移動サイズの設定
	if (      ptCur.x < ptTL.x            ) { offX = -20 ; }
	else if ( ptCur.x > ptTL.x+dim.width  ) { offX =  20 ; }
	if (      ptCur.y < ptTL.y            ) { offY = -20 ; }
	else if ( ptCur.y > ptTL.y+dim.height ) { offY =  20 ; }

	// 画面からはみ出ていたので移動
	if ( offX != 0 || offY != 0 ) {
		Point pt = new Point();
		pt.x = ptTL.x + offX ;
		pt.y = ptTL.y + offY ;
		// 画面サイズより外に出た場合は移動しない
		if (    pt.x<0 )                    { pt.x=0 ; }
		else if(pt.x>(dc.width-dim.width) ) { pt.x=dc.width-dim.width ; }
		if (    pt.y<0 )                    { pt.y=0 ; }
		else if(pt.y>(dc.height-dim.height)){ pt.y=dc.height-dim.height;}
		port.setViewPosition( pt );

	}

	// 追加Gobの削除
	// 追加Gobは、repaintから呼ばれるgetMovingPath()で作成され、
	// GobManager.paintで描画される
	GobManager.list(mEditor.getPanelId(),GobManager.HIGHLIGHT).removeAllElements();

	if ( GobManager.getCurrent(mEditor.getPanelId()).size() > 0 ) {
		mEditor.getGridControler().setMoving( new ZPoint(evt.getPoint()),0 );
//		ZDebug.trace();
		mEditor.repaint();
	}
	}

	/**
	 * マウスが移動
	 * @param evt マウスイベント
	 */
	public void mouseMoved(MouseEvent evt){
//	ZDebug.trace("mouseMoved:"+evt.getPoint());
	int nPrev = GobManager.size(mEditor.getPanelId(),GobManager.HIGHLIGHT);
	updateCursor(evt);
	int nCur = GobManager.size(mEditor.getPanelId(),GobManager.HIGHLIGHT);
	// ハイライトに変化があったとき再描画
	if ( nPrev != 0 || nCur != 0 ) {
//		ZDebug.trace();
		mEditor.repaint();
	}
	}

	//## ユーティリティ ###############################################
	/**
	 * カーソル形状の変更
	 * @param evt マウスイベント
	 */
	public void updateCursor(MouseEvent evt){
	byte nMode = GOperation.NONE;

	// ハイライトGobの削除
	// ハイライトGobは、getSelectedPreMoveMode()で作成され、
	// GobManager.paintで描画される
	GobManager.list(mEditor.getPanelId(),GobManager.HIGHLIGHT).removeAllElements();

	// 新規作成、移動、拡大の場合はオペレーションの作成
	GElement ope = createOperation(evt);
	if ( ope != null ) {
		ZPoint transPt = ((DocElement)mEditor.getDocument().getRootElement(mEditor.getPanelId())).getDisplayScaledTransform(mEditor.getPanelId()).inverseTransform(new ZPoint(evt.getPoint()));
		nMode = ope.getPreMoveMode(transPt);
	}

	// 新規作成でない場合
	if ( nMode == GOperation.NONE ) {
		// 現在選択中要素にヒットするか？
		nMode = GobManager.getSelectedPreMoveMode(mEditor,new ZPoint(evt.getPoint()));
	}

	mEditor.setCursor(GOperation.getCursor(nMode));
	// ここでは再描画しない
	// ハイライトに変更があった場合は、呼び出し元で再描画する
	}

	/**
	 * 操作の取得
	 * @param evt マウスイベント
	 */
	public GElement createOperation(MouseEvent evt){
	GElement ope = null;
//	ZDebug.trace("SPC:"+ZInstance.prop().getProperty(KEY_VK_SPACE));

	String szState = ZInstance.prop().getProperty(DocBar.ACTION);

	// コントロールキーが押されている場合
	if ( evt.isControlDown() ) {
		if ( evt.isAltDown() && evt.isShiftDown() ) {
			ope = new GOperationZoom(false);	// Ctrl+Alt+Shift	縮小
		} else if ( evt.isAltDown() ) {
			ope = new GOperationZoom(true);		// Ctrl+Alt			拡大
		}
		// else 移動	// NOP

	// アルトキー、移動モード
	} else if ( evt.isAltDown() ) {
		ope = new GOperationMove();

	} else if ( szState.equals(DocBar.SELECT_ACTION) ) {
		// NOP
	} else if ( szState.equals(DocBar.MOVE_ACTION) ) {
		ope = new GOperationMove();
	} else if ( szState.equals(DocBar.ZOOM_ACTION) ) {
		// ALT : 縮小
		if ( evt.isShiftDown() ) {
			ope = new GOperationZoom(false);
		// : 拡大
		} else {
			ope = new GOperationZoom(true);
		}
	} else {
		ope = new GOperationCreate(mEditor.getDocument(),szState);
	}

	return ope;
	}

	//## アトリビュート################################################
    /**
     * コンテナ
     */
	protected DocPane	mEditor  = null ;

    /**
     * 移動開始点
     */
	protected ZPoint	mStartPoint = null ;

    /**
     * 移動モード
     */
	protected byte		mMoveMode   = GOperation.NONE ;

    /**
     * ZInstanceキー：スペースキーの状態
     */
	static protected String KEY_VK_SPACE = "uiadapter.key.space" ;
}
