/**
 * @(#)DocPane.java
 *
 * Copyright 2000 by Intelligent Technology Inc. All rights reserved.
 */
package jp.co.iti.fagot.doc;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

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

import javax.swing.JComponent;
import javax.swing.JViewport;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import javax.swing.text.JTextComponent;

import jp.co.iti.fagot.ZApp;
import jp.co.iti.fagot.IZApp;

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

import jp.co.iti.fagot.action.ZAction;

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

import jp.co.iti.fagot.gob.GobManager;
import jp.co.iti.fagot.gob.GElement;

import jp.co.iti.fagot.util.ZKeyMap;
import jp.co.iti.fagot.util.ZDebug;
/**
 * 描画ペイン
 *
 * @author  福島康治
 * @version 1.01 2000/12/01
 */
public class DocPane extends DocComponent {

	/**
	 * コンストラクタ
	 */
	public DocPane() {
	}

	/**
	 * コンストラクタ
	 * @param panel パネル識別子
	 */
	public DocPane(String panelId){
	super(panelId);
	}

	//## オーバライド #################################################
	/**
	 * 背景色の取得
	 */
	public Color getBackground() {
	Color bg = null;
	// 初期表示時(コンストラクタ内)ではパネルIdはnullになっているため、回避策
	if ( getDocument() != null ) {
		bg = getDocument().getBackground(getPanelId());
	}

	if ( bg == null ) {
		bg = super.getBackground() ;
	}
	return bg;
	}

	/**
	 * コンポーネントに最適なサイズの取得
	 */
	public Dimension getPreferredSize() {
	Dimension dim = null;
	// 初期表示時(コンストラクタ内)ではパネルIdはnullになっているため、回避策
	if ( getDocument() != null ) {
		dim = getDocument().getPreferredSize(getPanelId());
	}

	if ( dim == null ) {
		dim = super.getPreferredSize() ;
	}
	return dim;
	}

	//## 操作 ########################################################
	/**
	 * 画面中央の座標の取得
	 * @ret pt 中央の座標(論理座標)
	 */
	public ZPoint getCenter() {
	DocElement root = (DocElement)getDocument().getRootElement(getPanelId());

	if ( getParent() == null || !(getParent() instanceof JViewport) ) {
		return null;
	}

	JViewport port  = (JViewport)getParent();
	Point ptTL = port.getViewPosition();

	// 物理座標
	ZPoint ptCenter = new ZPoint( ptTL.x + port.getWidth()/2,
								  ptTL.y + port.getHeight()/2 );

	if ( root == null ) {
		return ptCenter;
	}

	// 論理座標に変換
	ZPoint ptret    = root.getDisplayScaledTransform(getPanelId()).inverseTransform(ptCenter);
	return ptret;
	}

	/**
	 * 画面の移動
	 * @param pt 中央の座標(論理座標)
	 */
	public void setCenter(ZPoint pt) {
	DocElement root = (DocElement)getDocument().getRootElement(getPanelId());
	ZPoint ptCenter = null;
	if ( root != null ) {
		ptCenter = root.getDisplayScaledTransform(getPanelId()).transform(pt);
	} else {
		ptCenter = pt;
	}

	setPaintMode(false);

	if ( getParent() == null || !(getParent() instanceof JViewport) ) {
		return;
	}
	JViewport port  = (JViewport)getParent();

	// 画面の左上はptCenterから画面の半分サイズ左上の位置
	Dimension dim = port.getSize();
	Dimension dc  = getSize();

	Point ptTL = new Point( (int)(ptCenter.getX() - dim.width/2 ),
							(int)(ptCenter.getY() - dim.height/2) ) ;

	// 位置補正
	if (     ptTL.x<0 )                    { ptTL.x=0 ; }
	else if( ptTL.x>(dc.width-dim.width) ) { ptTL.x=dc.width-dim.width ;}
	if (     ptTL.y<0 )                    { ptTL.y=0 ; }
	else if( ptTL.y>(dc.height-dim.height)){ ptTL.y=dc.height-dim.height;}

	port.setViewPosition( ptTL );
	updateCenter( pt );
	setPaintMode(true);
	}

	/**
	 * 画面中央の座標の保存
	 */
	public void updateCenter() {
	if ( mPainted && canPaint() ) {
		ZPoint pt = getCenter();
		updateCenter( pt );
	}
	}

	/**
	 * 画面中央の座標の保存
	 * @param pt 中央座標
	 */
	public void updateCenter(ZPoint pt) {
	DocElement root = (DocElement)getDocument().getRootElement(getPanelId());
	if ( root != null ) {
		XElementNS elm = root.getChildElement(".ViewInfo."+getPanelId(),null);
		elm.setDoubleAttr("DisplayX", pt.getX());
		elm.setDoubleAttr("DisplayY", pt.getY());
//		ZDebug.trace("UPDATE:"+root.getAttribute("Name"));
//		ZDebug.printStack();
//		ZDebug.trace("	upd XY:"+pt.getX()+","+pt.getY());
	}
	}

	/**
	 * 表示矩形の変更
	 * @param rt 矩形
	 */
	public void updateRect(ZRectangle rect) {
	double inset = 100;
	JViewport port = (JViewport)getParent();
	Dimension dim  = port.getSize();
	double dx = dim.width/ Math.max(dim.width,rect.getWidth()+inset*2);
	double dy = dim.height/Math.max(dim.height,rect.getHeight()+inset*2);
	double dz = Math.min(dx,dy);
	
	// ポートの再レイアウト
	String szPanelId = getPanelId();
	DocDocument doc = getDocument();
	DocElement root = (DocElement)doc.getRootElement(szPanelId);
	root.setDisplayScale( szPanelId, dz );
	port.doLayout();

	// 位置を中央寄せ
//	ZDebug.trace(" center:"+rect.getCenter());
	setCenter( rect.getCenter() );

	// 前回描画位置に戻さない
	mPainted = true;
	}

	//## 描画 ########################################################
	/**
	 * ドキュメントの設定
	 * @param doc ドキュメント
	 * @see JComponent#accessibleContext
	 */
	public void setDocument(DocDocument doc) {
	super.setDocument(doc);
	mPainted = false;
	updateGobs();
	}

	/**
	 * パネルの初期表示：描画位置を前回のところに戻す
	 */
	public void editRestart() {
	mPainted = false;
	}

	/**
	 * コンテンツが更新された
	 */
	public void updateGobs() {
	mUpdateGobs = true;
	}

	/**
	 * コンテンツが更新されたか？
	 */
	public boolean isUpdateGobs() {
	return mUpdateGobs;
	}

	/**
	 * レイアウトの最適化
	 */
	public void updateLayout() {
		ZRectangle rect = getGobRect();
		if ( rect != null ) updateRect(rect);
		repaint();
	
/*
	ZRectangle rect = null;
	Enumeration elm = gobs.elements();
	while ( elm.hasMoreElements() ) {
		GElement gob = (GElement)elm.nextElement();
		ZRectangle rt = gob.getTransformedBounds();
		if ( rect == null ) {
			rect = rt;
		} else {
			rect.union( rt );
		}
	}
	if ( rect != null ) {
		updateRect(rect);
	}
	repaint();
*/
	}
	
	private ZRectangle getGobRect(){
		ZRectangle rect = null;
		Vector gobs = getDocument().createGobs(getPanelId());
		Enumeration elm = gobs.elements();
		while ( elm.hasMoreElements() ) {
			GElement gob = (GElement)elm.nextElement();
			ZRectangle rt = gob.getTransformedBounds();
			if ( rect == null )	rect = rt;
			else				rect.union(rt);
		}
		
		return rect;
	}

	/**
	 * 描画エレメントの更新
	 */
	public void createGobs() {
	// トランザクションの開始
	DocDocument doc = getDocument();
	boolean isTran = doc.isTransaction();
	if ( !isTran ) {
		doc.beginTransaction();
	}

	Vector gobs = GobManager.getElement(getPanelId());

	gobs.removeAllElements();
	gobs.addAll( doc.createGobs(getPanelId()) );
	mUpdateGobs = false;

	// コミット
	if ( !isTran ) {
		doc.commitPlus();
	}

	if ( !mPainted ) {
		mPainted = true;
		Dimension dim = doc.getLogicalSize();
		ZPoint pt = new ZPoint(dim.getWidth()/2,dim.getHeight()/2);
		// 初回描画時は前回表示位置に移動
		DocElement root = (DocElement)doc.getRootElement(getPanelId());
		if ( root != null ) {
			XElementNS elm = root.findChildElement(".ViewInfo."+getPanelId(),null);
/*
			if ( elm != null ) {
				pt.setX( elm.getDoubleAttr("DisplayX", pt.getX()) );
				pt.setY( elm.getDoubleAttr("DisplayY", pt.getY()) );
			}
*/
			String dspx = elm.getAttribute("DisplayX");
			if ((dspx == null) || (dspx.length() <= 0)){
				updateLayout();
				return;
			}else {
				pt.setX( elm.getDoubleAttr("DisplayX", pt.getX()) );
				pt.setY( elm.getDoubleAttr("DisplayY", pt.getY()) );
			}
//			ZDebug.trace("SetCenter;"+pt.toString());
		}
		setCenter(pt);
	}
	}

	/**
	 * 再描画モードの設定
	 * @param mode 可不可フラグ
	 */
	public void setPaintMode(boolean mode) {
	mCanPaint = mode;
	}

	/**
	 * 再描画モードの取得
	 */
	public boolean canPaint() {
	return mCanPaint;
	}

	//## コンポーネント ################################################
	/**
	 * UIアダプタの作成
	 * @return UIアダプタ
	 */
	public DocUIAdapter createUIAdapter() {
	return new DocUIAdapter(this);
	}

	/**
	 * グリッドコントローラの作成
	 * @param grid グリッドコントローラ
	 */
	public DocGridControler createGridControler() {
	DocGridControler grid = new DocGridControler();
	grid.setEditor( this ) ;
	return grid;
	}

	//## キーマップ ####################################################
	/**
	 * デフォルトキーマップを生成します
	 * @param app アプリケーション
	 */
	public Vector createDefaultKeyMap(IZApp app) {
	Vector keyMap = new Vector();
	return keyMap ;
	}

	/**
	 * キーマップを生成します
	 */
	public void setKeyMap(Vector keymap) {

	Enumeration elm = elm=keymap.elements() ;
	while( elm.hasMoreElements() ) {
		ZKeyMap key = (ZKeyMap)elm.nextElement();
		ZAction act = (ZAction)key.getAction();
		// インプットマップとアクションマップの対応づけ
		getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
			key.getKeyStroke(), act.getActionName());
		getActionMap().put(act.getActionName(), act);
	}
	}

	/**
	 * キーマップを削除します
	 */
	public void removeKeyMap(Vector keymap) {

	Enumeration elm = elm=keymap.elements() ;
	while( elm.hasMoreElements() ) {
		ZKeyMap key = (ZKeyMap)elm.nextElement();
		ZAction act = (ZAction)key.getAction();
		// インプットマップとアクションマップの対応づけ
		getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(
			key.getKeyStroke());
		getActionMap().remove(act.getActionName());
	}
	}

	//## 文字列編集 ####################################################
	/**
	 * 編集コンポーネントの設定
	 * @param elm  編集対象エレメント
	 * @param rect 位置(論理座標)
	 * @param isMulti 複数行編集の有無
	 */
	public void setEditElement(DocElement elm, ZRectangle rect,boolean isMulti) {
	DocElement root = (DocElement)getDocument().getRootElement(getPanelId());
	ZRectangle rtTrans = new ZRectangle(root.getDisplayScaledTransform(getPanelId()).transform(rect).getBounds2D());

	// キーマップの削除
	removeKeyMap( createDefaultKeyMap(ZApp.getTheApp()) );

	// エレメントの保存と入力コンポーネントの作成
	mEditElement = elm ;
	mEditRect    = rect;
	mEditComponent = null;
	if(isMulti) {
		mEditComponent = new JTextArea();
	} else {
		mEditComponent = new JTextField();
		((JTextField)mEditComponent).addActionListener( new DocEditActionListener() );
	}
	String szKey = "Doc "+rtTrans.getX()+" "+rtTrans.getY()+" "+rtTrans.getWidth()+" "+rtTrans.getHeight();
	mEditComponent.setText( elm.getText() ) ;
	add(szKey,mEditComponent);
	validate();
	}

	/**
	 * 編集終了
	 * @ret 編集がキャンセルされたか？
	 */
	public boolean editEnd() {
	if ( mEditComponent != null ) {
		// トランザクション開始
		String szText = mEditElement.getText();
		getDocument().beginTransaction();

		mEditElement.setText( mEditComponent.getText() );
		remove( mEditComponent );
		mEditComponent = null;
		mEditElement   = null;
		mEditRect      = null;

		// トランザクション終了
		if ( szText.length() > 0 ) {
			getDocument().commit();
		} else {
			getDocument().commitPlus();
		}

		updateGobs();
		validate();
		repaint();

		// キーマップの再設定
		setKeyMap( createDefaultKeyMap(ZApp.getTheApp()) );
		requestFocus();

		return true;
	}
	return false;
	}

	//## 文字列編集アクションリスナー #################################
	/**
	 * 文字列編集時、リターンキーで確定
	 */
	protected class DocEditActionListener implements ActionListener {
	/**
	 * アクションの実行
	 * @param evt イベント情報
	 */
	public void actionPerformed(ActionEvent evt) {
	editEnd();
	}
	}

	//## アトリビュート################################################
    /**
     * ドキュメントが更新されて、再描画されていない
     */
	protected boolean 		mUpdateGobs = true;

    /**
     * 初回描画フラグ
     */
	protected boolean 		mPainted = false;

    /**
     * 描画可能か？
     * 操作中の場合は、描画を不可に設定する
     */
	protected boolean 		mCanPaint = true;

	/**
	 * 編集対象エレメント
	 */
	protected DocElement 	mEditElement = null;

	/**
	 * 編集矩形
	 */
	protected ZRectangle 	mEditRect = null;

	/**
	 * 編集コンポーネント
	 */
	protected JTextComponent mEditComponent = null;
}
