/*
 * @(#)MapModel.java
 *
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 *
 * RID-X : Map paint model
 *
 * @author  Intelligent Technology Inc.
 * @version 1.01 2001/12/10
 */
package jp.co.nri.rid.doc;

import java.awt.Shape;
import java.awt.Dimension;

import java.awt.geom.AffineTransform;

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

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

import jp.co.iti.fagot.util.ZDebug;

import jp.co.nri.Rid;
/**
 * Map描画用モデルクラス
 */
public class MapModel {

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

	//## 変換 #########################################################
	/**
	 * ページ描画オブジェクトに変換
	 * @param major メジャー番号
	 * @param minor マイナー番号
	 * @param szShape 形状
	 */
	public Shape createPageShape( double major, double minor, String szShape ) {
	GRectShape gRect = new GRectShape();
	gRect.setShape(szShape);
	gRect.setRect( mPageRect );
	return createShape( gRect.getPath(), major, minor );
	}

	/**
	 * シナリオ描画オブジェクトに変換
	 * @param major メジャー番号
	 * @param minor マイナー番号
	 */
	public Shape createScenarioShape( double major, double minor ) {
	ZRectangle rt = new ZRectangle(mPageRect);
	double  inset = 20;
	rt.setX(rt.getX()-inset);
	rt.setY(rt.getY()-inset);
	rt.setWidth(rt.getWidth()+inset*2);
	rt.setHeight(rt.getHeight()+inset*2);
//	return createShape( rt.getEllipse2D(), major, minor );
	return createShape( rt, major, minor );
	}

	/**
	 * タイトル描画オブジェクトに変換
	 * @param major メジャー番号
	 * @param minor マイナー番号
	 */
	public Shape createTitleShape( double major, double minor ) {
	return createShape( mTitleRect, major, minor );
	}

	/**
	 * アクセスカウント描画オブジェクトに変換
	 * 高さの最大値：mPageRectの2倍
	 * 　　　最小値：1
	 * 求める高さ h は
	 * h ： count = mPageRect.getHeight()*2 ： mAccessMax
	 *
	 * @param major メジャー番号
	 * @param minor マイナー番号
	 * @param acc   アクセスカウント
	 */
	public Shape[] createAccessShapes( double major, double minor, AccessCounts acc ) {
	ZRectangle rt = new ZRectangle(mPageRect);

	// 高さは、ページの2.5倍を最大値とする
	double height = rt.getHeight()*2.5 ;
	rt.setY(rt.getY()+rt.getHeight()-height);
	rt.setHeight(height);

	// 幅はページより両端5pix細い
	rt.setX(rt.getX()+5);
	rt.setWidth(rt.getWidth()-5*2);

	ZRectangle[] rts = mAcc.createAccessRects( rt, acc ) ;
	if ( rts == null ) {
		return null ;
	}
	Shape[] shs = new Shape[rts.length];
	for ( int nAt=0 ; nAt<rts.length ; nAt++ ) {
		shs[nAt] = createShape( rts[nAt], major, minor );
	}
	return shs;
	}

	/**
	 * タイトル描画オブジェクトに変換
	 * @param sh    形状
	 * @param major メジャー番号
	 * @param minor マイナー番号
	 */
	public Shape createShape( Shape sh, double major, double minor ) {
	Shape shObj = mAtObject.createTransformedShape( sh ) ;
	ZPoint ppt  = modelToView( new ZPoint(major,minor) );
	AffineTransform atOffset = new AffineTransform(1,0,0,1,ppt.getX(),ppt.getY());
	return atOffset.createTransformedShape(shObj);
	}

	/**
	 * リンクポイントの取得
	 * @param major メジャー番号
	 * @param minor マイナー番号
	 */
	public ZPoint getLinkPoint( double major, double minor ) {
	ZPoint pt  = modelToView( new ZPoint(major,minor) );
	pt.translate( getLinkPointOffset() );
	return pt;
	}

	/**
	 * 子供へのリンクの中間ポイントの取得
	 * @param major メジャー番号
	 * @param minor マイナー番号
	 */
	public ZPoint getLinkHarfPoint( double major, double minor ) {
	return getLinkPoint( major+0.5, minor );
	}

	/**
	 * リンクポイントオフセットの取得
	 */
	public ZPoint getLinkPointOffset() {
	ZPoint ptLink = new ZPoint( mPageRect.getWidth()*mLinkOffset.getX(),
								mPageRect.getHeight()*mLinkOffset.getY() );

	// ページ変形にあわせてシャーリング変換
	ZPoint pt = new ZPoint();
	mAtObject.transform( ptLink, pt );
	return pt;
	}


	//## 基本変換 #####################################################
	/**
	 * Major-Minor座標→XY座標変換
	 * @param  pt x:Major, y:Minor
	 * @return    x:X座標, y:Y座標(相対位置)
	 */
	public ZPoint modelToCoord(ZPoint pt) {
	ZPoint ptTrans = new ZPoint();
	if ( getCoordTransform() != null ) {
		// ページ変形にあわせてシャーリング変換
		getCoordTransform().transform(pt, ptTrans);
	} else {
		// CircleMapの場合 単位ベクトルは [cos(minor),sin(minor)]
		// 単位ベクトルをmajor倍する
		double dOff = getCircleOffset();
		ptTrans.setX( Math.cos(pt.getY())*(pt.getX()-1)*dOff );
		ptTrans.setY( Math.sin(pt.getY())*(pt.getX()-1)*dOff );
//		ZDebug.trace("MM["+pt.getX()+","+pt.getY()+"]=Coord:"+ptTrans);
	}
/*
	if ( boo ) {
		boo = false;
		
		for ( int nIndex=10; nIndex<360 ; nIndex+=10 ) {
			double mm   = (double)nIndex;
			double d0   = (2*Math.PI/360)*mm ;
		
			double dX = Math.cos(d0);
			double dY = Math.sin(d0);
			double ata = Math.atan(dY/dX);
			if ( dX < 0 ) {
				ata += Math.PI;
			} else if ( dY < 0 ) {
				ata += Math.PI*2;
			}
			ZDebug.trace("minor:"+mm+"  minor:"+d0+" ata:"+ata+" dX:"+dX+" dY"+dY+"  "+((ata==d0)?"●":"×"));
//		ZDebug.trace("tan:"+Math.tan(dX/dY));
//		ZDebug.trace("tan:"+Math.tan(dY/dX));
//		ZDebug.trace("atan:"+Math.atan(dX/dY));
//		ZDebug.trace("atan:"+Math.atan(dY/dX));
		}
	}
*/
	return ptTrans;
	}


//	static boolean boo = true;

	/**
	 * Major-Minor座標→XY座標変換
	 * @param  pt x:Major, y:Minor
	 * @return    x:X座標, y:Y座標(開始位置を考慮した絶対座標)
	 */
	public ZPoint modelToView( ZPoint pt ) {
	ZPoint ptTrans = modelToCoord(pt);
//	ptTrans.translate( mBasePoint );
	ptTrans.translate( new ZPoint( mBasePoint.getX()-mBaseOffset.getX(),
								   mBasePoint.getY()-mBaseOffset.getY() ) );
	return ptTrans;
	}

	/**
	 * XY座標→Major-Minor座標変換
	 * @param pt x:X座標, y:Y座標(相対位置)
	 * @return   x:Major, y:Minor
	 */
	public ZPoint coordToModel(ZPoint pt) {
	ZPoint ptTrans = new ZPoint();
	if ( getCoordTransform() != null ) {
		try {
			// ページ変形にあわせてシャーリング逆変換
			getCoordTransform().inverseTransform( pt, ptTrans );
		} catch (Exception exc) {
			ZDebug.err("inverse transform failed");
		}
	} else {
		// X座標は、ptベクトルの長さ
		double dOff = getCircleOffset();
		ptTrans.setX(Math.sqrt((pt.getX()*pt.getX()+pt.getY()*pt.getY())/(dOff*dOff))+1);

		// Y座標は逆正接 (アークタンジェント）であるが、座標の位置により PI,2PI を加算
		//                |
		//   atan(y/x)+pi | atan(y/x)
		//                |
		//  --------------+-----------------
		//                |
		//   atan(y/x)+pi | atan(y/x)+2pi
		//                |
		double ata = Math.atan(pt.getY()/pt.getX());
		if ( pt.getX() < 0 ) {
			ata += Math.PI;
		} else if ( pt.getY() < 0 ) {
			ata += Math.PI*2;
		}
		ptTrans.setY(ata);

//		ZDebug.trace("MM["+pt.getX()+","+pt.getY()+"]=Coord:"+ptTrans);
	}
	return ptTrans;
	}

	/**
	 * XY座標→Major-Minor座標変換
	 * @param pt x:X座標, y:Y座標(開始位置を考慮した絶対座標)
	 * @return   x:Major, y:Minor
	 */
	public ZPoint viewToModel( ZPoint pt ) {
//	pt.translate( -mBasePoint.getX(), -mBasePoint.getY() );
	pt.translate( -(mBasePoint.getX()-mBaseOffset.getX()),
				  -(mBasePoint.getY()-mBaseOffset.getY()) );
	ZPoint ptTrans = coordToModel(pt);
	return ptTrans;
	}

	//## プロパティ ###################################################
	/**
	 * アクセスカウントの設定
	 * @param acc 最大値
	 */
	public void setAccessCounts( AccessCounts acc ) {
	if ( acc != null ) {
		mAcc = acc;
	}
	}

	/**
	 * 印刷開始位置：(0,0)座標の設定
	 */
	public void setBasePoint( ZPoint pt ) {
	mBasePoint = pt;
	}

	/**
	 * リンクオフセットの設定
	 */
	public void setLinkOffset( ZPoint pt ) {
	mLinkOffset = pt;
	}

	/**
	 * オブジェクト変換ルーチンの設定
	 */
	public void setObjectTransform(AffineTransform at) {
	mAtObject = at;
	}

	/**
	 * 座標変換ルーチンの設定
	 */
	public void setCoordTransform(AffineTransform at) {
	mAtCoord = at;
	}

	/**
	 * 座標変換ルーチンの取得
	 */
	protected AffineTransform getCoordTransform() {
	AffineTransform at = null;
	if ( mAtCoord != null ) {
		at = new AffineTransform( mAtCoord );
		at.scale( mScale,mScale );
	}
	return at;
	}

	/**
	 * Circleオフセットの取得
	 */
	public double getCircleOffset() {
	return mCircleOffset*mScale;
	}

	/**
	 * オフセットの初期化
	 * 再計算前に初期化され、下記fitToPanelで再設定される
	 */
	public void clearOffset() {
	mScale = 1;
	mBaseOffset = new ZPoint(0,0);
	}

	/**
	 * パネルにちょうど合うようなサイズに補正
	 * @param rect 現状の描画矩形
	 * @param dim  パネルサイズ
	 */
	public ZRectangle fitToPanel( ZRectangle rect, Dimension dim ) {
	// rectが収まるdimの中央を中心とした矩形を取得する
//	ZDebug.trace("  rect:"+rect+" dim:"+dim);

	// あふれ領域の調査
	// 縦方向
	double dN = Math.max(0,-rect.getY());
	double dS = Math.max(0,rect.getY()+rect.getHeight()-dim.height);
	double dV = Math.max(dN,dS);
//	ZDebug.trace("  dV:"+dV+" dN:"+dN+" dS:"+dS);

	// 横方向
	double dW = Math.max(0,-rect.getX());
	double dE = Math.max(0,rect.getX()+rect.getWidth()-dim.width);
	double dH = Math.max(dW,dE);
//	ZDebug.trace("  dH:"+dH+" dW:"+dW+" dE:"+dE);

	ZRectangle rt = new ZRectangle(rect);
	// あふれている場合は補正
	if ( dV > 0 || dH > 0 ) {
		// 縮尺の計算
		mScale = Math.min( dim.height/rect.getHeight(),
						   dim.width /rect.getWidth() );
		if ( mScale > 1 ) {
			mScale = 1;
		}
//		ZDebug.trace("MapScale:"+mScale);

		// 描画開始位置の計算
		mBaseOffset.setX(mBasePoint.getX()-(mBasePoint.getX()-rect.getX())*mScale);
		mBaseOffset.setY(mBasePoint.getY()-(mBasePoint.getY()-rect.getY())*mScale);
//		ZDebug.trace("Offset:"+mBaseOffset);

		if ( mScale < 1 ) {
			// 縮小の場合、全て
			rt = new ZRectangle(0,0,dim.width,dim.height);
		} else {
			// 等倍
			rt = new ZRectangle(0,0,rect.getWidth(),rect.getHeight());
		}
	}

	return rt;
	}

	//## アトリビュート ###############################################
	/**
	 * ページ形状
	 */
	protected ZRectangle mPageRect = new ZRectangle(0,0,30,40);

	/**
	 * タイトル形状
	 */
	protected ZRectangle mTitleRect = new ZRectangle(0,0,30,10);

	/**
	 * 描画開始位置
	 */
	protected ZPoint mBasePoint = new ZPoint( 4000*Rid.PANELSIZE, 3000*Rid.PANELSIZE );

	/**
	 * 描画開始位置の補正値
	 */
	protected ZPoint mBaseOffset = new ZPoint( 0, 0 );

	/**
	 * リンクオフセット
	 */
	protected ZPoint mLinkOffset = new ZPoint( 0.5, 0.5 );

	/**
	 * オブジェクト変換ルーチン
	 */
	protected AffineTransform mAtObject = new AffineTransform(1,0,0,1,0,0);

	/**
	 * 座標変換ルーチン
	 */
	protected AffineTransform mAtCoord = new AffineTransform(100,0,0,100,0,0);

	/**
	 * Circleの場合のオフセット
	 */
	protected double mCircleOffset = 100;

	/**
	 * 座標変換縮小率
	 */
	protected double mScale = 1;

	/**
	 * アクセスカウント配列
	 */
	protected AccessCounts mAcc = new AccessCounts();

	/**
	 * アクセスカウント最大値
	 */
	protected int mAccessMax = 1000;
}
