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

import java.awt.Shape;

import java.awt.geom.AffineTransform;

import java.util.Stack;
import java.util.Enumeration;

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

import jp.co.iti.fagot.gob.PTransform;

import jp.co.iti.fagot.util.ZDebug;
import jp.co.iti.fagot.util.ZReverseEnumeration;
/**
 * デフォルトグリッドコントローラー
 * @author  福島康治
 * @version 1.01 2001/01/07
 */
public class DocGridControler {

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

	/**
	 * コンストラクタ
	 * @param state グリッドの{有効|無効}設定
	 */
	public DocGridControler(boolean state){
		setEnable( state ) ;
	}

	/**
	 * グリッド状態の設定
	 * @param state グリッドの{有効|無効}設定
	 */
	public void setEnable(boolean state){
	mState = state ;
	}

	/**
	 * グリッド間隔の設定
	 * @param nX 水平グリッド間隔
	 * @param nY 垂直グリッド間隔
	 */
	public void setGrid(double nX, double nY){
	mX = nX ;
	mY = nY ;
	}

	/**
	 * 移動開始
	 * @param pt 開始位置
	 * @param nMode 移動モード
	 */
	public void setMoveStart(ZPoint pt,byte nMode){
	mStartPoint = pt ;
	mMoveMode   = nMode ;
	}

	/**
	 * 移動中
	 * @param pt 開始位置
	 * @param mask 移動マスク
	 */
	public void setMoving(ZPoint pt,int mask) {
	mMovePoint = pt ;
	mMoveMask  = mask ;
	}

	//## 変換 #########################################################
	/**
	 * 変換を末尾に追加
	 * @param trans 座標変換
	 */
	public void pushTransform(PTransform trans){
	mTrans.push(trans);
	createHashTransform();
	}

	/**
	 * 末尾の変換を除去
	 */
	public void popTransform(){
//	ZDebug.trace("Pop:"+mTrans.size());
	mTrans.pop();
	createHashTransform();
	}

	/**
	 * ハッシュ用座標変換の作成
	 */
	protected void createHashTransform() {
	// 物理座標への変換
	mHashTrans = null;
	Enumeration elm = new ZReverseEnumeration(mTrans) ;
	while ( elm.hasMoreElements() ) {
		PTransform     gtrans = (PTransform)elm.nextElement();
		if ( mHashTrans == null ) {
			mHashTrans = new PTransform(gtrans);
		} else {
			mHashTrans.add( gtrans );
		}
	}

	// 論理座標への変換
	mHashTransLP = null;
	elm = new ZReverseEnumeration(mTrans) ;
	PTransform  prev = (PTransform)elm.nextElement();
	while ( elm.hasMoreElements() ) {
		PTransform     gtrans = (PTransform)elm.nextElement();
		if ( mHashTransLP == null ) {
			mHashTransLP = new PTransform(prev);
		} else {
			mHashTransLP.add( prev );
		}
		prev = gtrans;
	}

	// ローカル座標への変換
	mHashInverse = null;
	mHashInverseZone = null;
	elm = mTrans.elements() ;
	while ( elm.hasMoreElements() ) {
		PTransform     gtrans = (PTransform)elm.nextElement();
		if ( mHashInverse == null ) {
			mHashInverse = new PTransform(gtrans);
			mHashInverseZone = new PTransform(gtrans);
		} else {
			mHashInverse.add( gtrans );
		}
	}

	}

	//## 座標変換######################################################
	/**
	 * 座標変換(Point)
	 */
	public ZPoint transform(ZPoint point){
	ZPoint pt = new ZPoint( point );
	if ( mHashTrans != null ) {
		pt = mHashTrans.transform( pt );
	}
	return pt;
	}

	/**
	 * 座標変換(Point)
	 * 座標変換スタックの一番下にある変換は物理座標→論理座標系への変換であるので
	 * 一番最後の変換しないで論理座標を取得する
	 */
	public ZPoint transformToLP(ZPoint point){
	ZPoint pt = new ZPoint( point );
	if ( mHashTransLP != null ) {
		pt = mHashTransLP.transform( pt );
	}
	return pt;
	}

	/**
	 * 座標変換(Shape)
	 */
	public Shape transform(Shape sp){
	Shape out = sp;
	if ( mHashTrans != null ) {
		out = mHashTrans.getTransform().createTransformedShape( sp );
	}
	return out;
	}

	/**
	 * 座標変換
	 * 文字列描画において、高さを算出する時などに利用するため、
	 * Y座標のスケール変換のみを適用する
	 * オフセット変換、シャーリング変換は無視
	 * @param dy Y座標
	 */
	public double transformY(double dy){
	double transy = dy;
	if ( mHashTrans != null ) {
		transy *= mHashTrans.getTransform().getScaleY();
	}
	return transy;
	}

	/**
	 * 座標逆変換(Point)
	 */
	public ZPoint inverseTransform(ZPoint point){
	ZPoint pt = new ZPoint( point );
	if ( mHashInverse != null ) {
		pt = mHashInverse.inverseTransform( pt );
	}
	return pt;
	}

	/**
	 * 変換の取得
	 * @ret AffineTransform
	 */
	public AffineTransform getTransform(){
	if ( mHashInverse != null ) {
		return mHashInverse.getTransform();
	}
	return null;
	}

	/**
	 * 倍率のみ変換の取得
	 * @ret AffineTransform
	 */
	public AffineTransform getScaleTransform(){
	if ( mHashInverse != null ) {
		return mHashInverse.getScaleTransform();
	}
	return null;
	}


	//## 値 ###########################################################
	/**
	 * 起点の取得
	 */
	public ZPoint getStartPoint() {
	return inverseTransform( mStartPoint ) ;
	}

	/**
	 * 移動点の取得
	 */
	public ZPoint getMovePoint() {
	return inverseTransform( mMovePoint ) ;
	}

	/**
	 * 移動オフセットの取得
	 */
	public ZPoint getOffset() {
	return getMovePoint().minus( getStartPoint() ) ;
	}

	/**
	 * 移動オフセットの取得
	 */
	public ZPoint getPhigicalOffset() {
	return mMovePoint.minus( mStartPoint ) ;
	}

	/**
	 * 移動モードの設定
	 * 特殊用途のために、移動モードを変更するときにしようする
	 */
	public void setMoveMode( byte mode ) {
	mMoveMode = mode;
	}

	/**
	 * 移動モードの取得
	 */
	public byte getMoveMode() {
	return ( mMoveMode ) ;
	}

	/**
	 * 移動マスクの取得
	 */
	public int getMoveMask() {
	return ( mMoveMask ) ;
	}

	//## グリッドの取得 ###############################################
	/**
	 * 起点グリッドの取得
	 */
	public ZPoint getStartGrid() {
	return ( getGrid( getStartPoint() ) ) ;
	}

	/**
	 * 移動グリッドの取得
	 */
	public ZPoint getMoveGrid() {
	return ( getGrid( getMovePoint() ) ) ;
	}

	/**
	 * 移動グリッドの取得Zone用
	 */
	public ZPoint getMoveGridZone() {
//	ZDebug.trace("mMovePoint;"+mMovePoint);
	ZPoint pt = new ZPoint( mMovePoint );
/*
	ZPoint pt2 = new ZPoint( mMovePoint );
	ZPoint pt3 = new ZPoint( mMovePoint );
	ZPoint pt4 = new ZPoint( mMovePoint );
	if ( mHashInverse != null ) {
		pt = mHashInverse.inverseTransform( pt );
	}
	ZDebug.trace();
*/
	Enumeration elm = mTrans.elements() ;
	while ( elm.hasMoreElements() ) {
		PTransform     gtrans = (PTransform)elm.nextElement();
//		ZDebug.trace("trans;"+gtrans);
		pt = gtrans.inverseTransform(pt);
//		ZDebug.trace("trans;"+pt);
	}
/*
	ZDebug.trace();
	elm = new ZReverseEnumeration(mTrans) ;
	while ( elm.hasMoreElements() ) {
		PTransform     gtrans = (PTransform)elm.nextElement();
		ZDebug.trace("trans;"+gtrans);
		pt3 = gtrans.inverseTransform(pt3);
		ZDebug.trace("trans;"+pt3);
	}
	ZDebug.trace("mTrans;"+pt);
	ZDebug.trace("mTrans2;"+pt2);
	ZDebug.trace("mTrans2;"+pt3);
*/
	return ( getGrid( pt ) ) ;
	}

	/**
	 * 移動オフセットグリッドの取得
	 */
	public ZPoint getOffsetGrid() {
	return ( getGrid( getOffset() ) ) ;
	}

	/**
	 * グリッドの取得
	 */
	public ZPoint getGrid(ZPoint pt) {
	return new ZPoint(getGrid(HORZ,pt.getX()),getGrid(VERT,pt.getY())) ;
	}

	/**
	 * グリッドの取得
	 */
	public double getGrid(byte mode, double val ) {
	if ( mState ) {
		double nGrid = (mode==HORZ) ? mX : mY ;
	
		// 近似点の取得
		double nNear  = (double)((int)( val/nGrid ))*(int)(nGrid*10)/10 ;
		double nNear2 = (double)((int)( val/nGrid )+1)*(int)(nGrid*10)/10 ;
		return ( ((nNear2-val)>(val-nNear)) ? nNear : nNear2 ) ;
	}
	return ( val );
	}

	/**
	 * グリッドの取得
	 * 指定した座標より大きい点、または小さい点
	 */
	public double getOverGrid(byte mode, double val, int over ) {
	if ( mState ) {
		double nGrid = (mode==HORZ) ? mX : mY ;
	
		// 近似点の取得
		if ( over == -1 ) {
			double nNear  = (double)((int)( val/nGrid ))*(int)(nGrid*10)/10 ;
			return nNear;
		} else {
			double nNear2 = (double)((int)( val/nGrid )+1)*(int)(nGrid*10)/10 ;
			return nNear2;
		}
	}
	return ( val );
	}

	//## エディタ #####################################################
	/**
	 * エディタの設定
	 * @param editor コンテナ
	 */
	public void setEditor(DocPane editor) {
	mEditor = editor;
	}

	/**
	 * エディタの取得
	 * @return コンテナ
	 */
	public DocPane getEditor() {
	return mEditor;
	}

	//## アトリビュート################################################
	/**
	 * グリッド定義(X軸)
	 */
	protected double	mX		= 10 ;

	/**
	 * グリッド定義(X軸)
	 */
	protected double	mY		= 10 ;

	/**
	 * グリッドの有功
	 */
	protected boolean	mState	= true ;

	/**
	 * 起点
	 */
	protected ZPoint	mStartPoint	= new ZPoint(0,0) ;

	/**
	 * 移動点
	 */
	protected ZPoint	mMovePoint	= new ZPoint(0,0) ;

	/**
	 * 移動モード
	 */
	protected byte		mMoveMode   = 0 ;

	/**
	 * 移動マスク
	 */
	protected int		mMoveMask	= 0 ;

	/**
	 * 方向定義：横
	 */
	final static protected byte	HORZ	= 0 ;

	/**
	 * 方向定義：縦
	 */
	final static protected byte	VERT	= 1 ;

    /**
     * 座標変換
     */
	protected Stack			mTrans = new Stack();

	/**
	 * エディタ
	 */
	protected DocPane mEditor = null ;

	/**
	 * ハッシュ用座標変換
	 */
	protected PTransform mHashTrans = null ;

	/**
	 * ハッシュ用座標変換：論理座標系
	 */
	protected PTransform mHashTransLP = null ;

	/**
	 * ハッシュ用座標変換：逆変換
	 */
	protected PTransform mHashInverse = null ;

	/**
	 * ハッシュ用座標変換：逆変換(DisplayScaleのみ)
	 */
	protected PTransform mHashInverseZone = null ;
}
