/**
 * @(#)GRootSVG.java
 * Copyright 2001 
 * @author  Intelligent Technology Inc.
 * @version 0.00 2001/08/14
 */
package jp.co.iti.fagot.xml;

/* JAVA */
import	java.util.Enumeration;
import	java.util.Vector;
import	java.awt.geom.PathIterator;

import	org.w3c.dom.*;

/* fagot */
import	jp.co.iti.fagot.gob.GElement;
import	jp.co.iti.fagot.gob.GPath;
import	jp.co.iti.fagot.gob.GLine;
import	jp.co.iti.fagot.gob.GRectangle;
import	jp.co.iti.fagot.gob.GEllipse;
import	jp.co.iti.fagot.gob.GString;
import	jp.co.iti.fagot.gob.PStyle;
import	jp.co.iti.fagot.gob.PTransform;
import	jp.co.iti.fagot.geom.ZRectangle;
import	jp.co.iti.fagot.geom.ZPoint;
import	jp.co.iti.fagot.geom.ZLine;
import	jp.co.iti.fagot.geom.ZPath;

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

import	jp.co.iti.fagot.*;
//import	jp.co.nri.rid.gob.GText;

import	jp.co.iti.fagot.xml.SVGWorker;


/**
 * GRootSVG
 */
public class GRootSVG	extends	SVGWorker{
	public double strokeWidthRatio = 1;
	
	private double myRootX;
	private double myRootY;
	private double myRootW;
	private double myRootH;

	/**
	 * デフォルトコンストラクタ
	 */
	public GRootSVG()
		throws
			 javax.xml.parsers.ParserConfigurationException
	{
		super(null ,null ,"800" ,"600" ,"0 0 10000 10000" ,"xMidYMid meet" ,null);


/*
		*** fuku 20020417 *** defは使用しない
		Element defs	= doc.createElement("defs");
		ZResource res = ZApp.getTheApp().getResource();
		SVGStyle style	= new SVGStyle();
		
		style.fill.fill	= ZGraphicUtil.stringToSystemColor(res.getString("unit.color.LinkUnit")).toLowerCase();
		try{
			String fill = style.fill.fill.toUpperCase();
			defs.appendChild(markerArrow("ForwardArrow" + fill	,"0 0 10 5 0 10"	,style ,false));
			defs.appendChild(markerArrow("BackwardArrow" + fill	,"0 5 10 0 10 10"	,style ,true));
		}
		catch(NullPointerException e){}
		
		style.fill.fill	= ZGraphicUtil.stringToSystemColor(res.getString("unit.color.StrongLink")).toLowerCase();
		try{
			String fill = style.fill.fill.toUpperCase();
			defs.appendChild(markerArrow("ForwardArrow" + fill	,"0 0 10 5 0 10"	,style ,false));
			defs.appendChild(markerArrow("BackwardArrow" + fill	,"0 5 10 0 10 10"	,style ,true));
		}
		catch(NullPointerException e){}
		
		style.fill.fill	= ZGraphicUtil.stringToSystemColor(res.getString("unit.color.NotExist")).toLowerCase();
		try{
			String fill = style.fill.fill.toUpperCase();
			defs.appendChild(markerArrow("ForwardArrow" + fill	,"0 0 10 5 0 10"	,style ,false));
			defs.appendChild(markerArrow("BackwardArrow" + fill	,"0 5 10 0 10 10"	,style ,true));
		}
		catch(NullPointerException e){}
		
		rootelement.appendChild(defs);
*/
	}
	
	private Element markerArrow(String id ,String points ,SVGStyle style ,boolean back){
		Element marker	= doc.createElement("marker");
		marker.setAttribute("id" ,id);
		marker.setAttribute("viewBox" ,"0 0 10 10");
		marker.setAttribute("refX" ,back ? "1" : "9");
		marker.setAttribute("refY" ,"5");
		marker.setAttribute("markerWidth"	,"12");
		marker.setAttribute("markerHeight"	,"12");
		marker.setAttribute("orient"		,"auto");
		marker.setAttribute("markerUnits"	,"userSpaceOnUse");
		Element arrow = doc.createElement("polygon");
		arrow.setAttribute("points" ,points);
		arrow.setAttribute("style" ,style.toString());
		marker.appendChild(arrow);
		
		return marker;
	}
	
	/**
	 * コンストラクタ
	 * @param gobs Gob群
	 */
	public GRootSVG(Vector gobs)
		throws
			 javax.xml.parsers.ParserConfigurationException
	{
	this();
	setGobs(gobs);
	}

	/**
	 * 描画エレメント群の設定
	 * @param gobs Gob群
	 */
	protected void setGobs(Vector gobs) {
		try{
			ZResource	res		= ZApp.getTheApp().getResource();
			double		ratio	= Double.parseDouble(res.getString("svg.line.width"));
			strokeWidthRatio	= ratio / 100;
		}
		catch(NumberFormatException	e){}
		catch(NullPointerException	e){}
/*
		//** fuku **
		// ViewBoxは後で設定するよう修正

		ZRectangle rootrect = root.getBoundsPreTransfar();
		double rootX = rootrect.getX();
		double rootY = rootrect.getY();
		double rootW = rootrect.getWidth();
		double rootH = rootrect.getHeight();
		
		if(800 > rootW && 600 > rootH){
			setWidth(rootW);
			setHeight(rootH);
		}
		setViewBox(rootX ,rootY ,rootW ,rootH);
*/
		
/*
		// RectのStyle.stroke
		SVGStyle rectStyle = new SVGStyle();
		rectStyle.stroke.stroke = "#000000";
		rectStyle.stroke.width = "0.1";
		
		// RectのStyle.fill
		rectStyle.fill.fill = "#e0e0e0";
		rectStyle.fill.opacity = "0.7";
*/
		ZRectangle rootrect = null;
		ZRectangle bounds   = null;

		// 各Element処理
		Enumeration enum = gobs.elements();
		while (enum.hasMoreElements()){
			GElement gob = (GElement)enum.nextElement();
			PTransform trans = gob.getTransform();

			/** fuku **/
			// 子供ができたので、背景子供と前景子供も描画するよう修正
			// 子供Gobのチェック
			Vector backGobs = gob.getBackChildren();
			Vector foreGobs = gob.getForeChildren();

			// 子供が存在すればグループ化
			Element parent	= rootelement;
			if ( (backGobs.size()+foreGobs.size()) > 0 ) {
				parent = doc.createElement("g");
				rootelement.appendChild(parent);
			}

			// 背面オブジェクトの追加
			Enumeration backElm = backGobs.elements();
			while ( backElm.hasMoreElements() ) {
				GElement item = (GElement)backElm.nextElement();
				bounds = addItem( parent, item, trans );
				if ( rootrect == null ) {
					rootrect = bounds;
				} else {
					rootrect.union( bounds );
				}
			}

			// 本体の追加
			bounds = addItem( parent, gob, null );
			if ( rootrect == null ) {
				rootrect = bounds;
			} else {
				rootrect.union( bounds );
			}

			// 前面オブジェクトの追加
			Enumeration foreElm = foreGobs.elements();
			while ( foreElm.hasMoreElements() ) {
				GElement item = (GElement)foreElm.nextElement();
				bounds = addItem( parent, item, trans );
				rootrect.union( bounds );
			}
		}

		// 表示領域の設定
		double inset = 20;
		myRootX = rootrect.getX() - inset;
		myRootY = rootrect.getY() - inset;
		myRootW = rootrect.getWidth()  + inset*2;
		myRootH = rootrect.getHeight() + inset*2;
		
		if(800 > myRootW && 600 > myRootH){
			setWidth(myRootW);
			setHeight(myRootH);
		}
		setViewBox(myRootX ,myRootY ,myRootW ,myRootH);
	}
	
	public String calcViewBox(double width ,double height){
		return
			myRootX + " " +
			myRootY + " " +
			myRootW + " " +
			myRootH;
	}
	
	/**
	//** fuku **
	// メソッド追加
	 * 描画エレメントの設定
	 * @param parent 親エレメント
	 * @param item   追加対象エレメント
	 * @param trans  祖先から受け継いだ座標変換
	 */
	protected ZRectangle addItem(Element parent, GElement item, PTransform trans) {
	// 座標の矩形計算
	ZRectangle bounds = item.getTransformedBounds();
	if ( trans != null ) {
		bounds = new ZRectangle(trans.transform(bounds).getBounds2D());
	}

			//GElement
			// PStyle getStyle()
			// FColor getFillColor();
			// FFloat getFillOpacity();
			// FString getFillRule();
			// FColor getStrokeColor();
			PStyle pstyle	= item.getStyle();
			SVGStyle style	= new SVGStyle();
			style.fill.fill		= pstyle.getFillColor().getString();
//			style.fill.opacity	= String.valueOf(pstyle.getFillOpacity().get());
			style.fill.rule		= pstyle.getFillRule().getString();
			style.stroke.stroke	= pstyle.getStrokeColor().getString();
			double strokewidth	= pstyle.getStroke().getWidth().get();
			strokewidth			= (0.0==strokewidth)?1.0:strokewidth;
			style.stroke.width	= String.valueOf(strokewidth*strokeWidthRatio);
			
			//GElement.GPath.GRectangle.GString
			// double getX() getY()
			// String getText()
			// String getFontName()
			// String getFontSize()
			if(item instanceof GString){
				GString g = (GString)item;
				
				style.text.dominantBaseline	= "hanging";
//				style.text.baselineShift	= "-100%";
				
				style.text.font.family		= g.getFontName();
				
				int sz = 0;
				if(null==style.text.font.family){
					ZResource res = ZApp.getTheApp().getResource();
					style.text.font.family = res.getString("app.font.name");
					sz = res.getInt("app.font.size" ,0);
				}
				else{
					try{ sz = Integer.parseInt(g.getFontSize() ,0); }
					catch(NullPointerException e){}
				
				}
				try{ style.text.font.size = String.valueOf(sz * trans.getTransform().getScaleY()); }
				catch(NullPointerException e){ style.text.font.size = String.valueOf(sz); }
				
				parent.appendChild(
					createText(String.valueOf(bounds.getX())
						,String.valueOf(bounds.getY())
						,g.getText() ,style));
			}
			
			// Ellipse
			//GElement.GPath.GRectangle.GEllips
			//ZRectangle getRect()
			else if(item instanceof GEllipse){
				double rx = bounds.getWidth()	/ 2.0;
				double ry = bounds.getHeight()	/ 2.0;
				double cx = bounds.getX() + rx;
				double cy = bounds.getY() + ry;
				
				parent.appendChild(
					createEllipse(String.valueOf(cx) ,String.valueOf(cy)
						,String.valueOf(rx) ,String.valueOf(ry) ,style));
			}
			
			// Rect
			//GElement.GPath.GRectangle
			//ZRectangle getRect()
			else if(item instanceof GRectangle){
				parent.appendChild(
					createRect(String.valueOf(bounds.getX())
						,String.valueOf(bounds.getY())
						,String.valueOf(bounds.getWidth())
						,String.valueOf(bounds.getHeight())
						,null ,null ,style));
			}
			
			//GElement.GPath.GLine
			// ZLine getLine()
			// double getX1()
			// double getX2()
			// double getY1()
			// double getY2()
			else if(item instanceof GLine){
				ZLine zline = ((GLine)item).getLine();
				ZPoint p1 = zline.getP1Z();
				ZPoint p2 = zline.getP2Z();

				PTransform mytrans = item.getTransform();
				p1 = mytrans.transform( p1 );
				p2 = mytrans.transform( p2 );

				if ( trans != null ) {
					p1 = trans.transform( p1 );
					p2 = trans.transform( p2 );
				}
				
				Element NormalLine = createLine(String.valueOf(p1.getX())
									   ,String.valueOf(p1.getY())
									   ,String.valueOf(p2.getX())
									   ,String.valueOf(p2.getY())
					 				   ,style);
				
/*
				boolean arrowed = false;
				String fill = style.stroke.stroke.toUpperCase();
				if (((GLine)item).isArrow(GLine.ARROW_FORWARD)){
					arrowed = true;
					style.misc.markerend = "url(#ForwardArrow" + fill + ")";
				}
				if (((GLine)item).isArrow(GLine.ARROW_BACKWARD)){
					arrowed = true;
					style.misc.markerstart = "url(#BackwardArrow" + fill + ")";
				}
				
				if(arrowed){
					Element gelem = doc.createElement("g");
					
					gelem.appendChild(NormalLine);
					gelem.appendChild(
						createLine(String.valueOf(p1.getX())
						   ,String.valueOf(p1.getY())
						   ,String.valueOf(p2.getX())
						   ,String.valueOf(p2.getY())
					 	   ,style));
					
					parent.appendChild(gelem);
				}
				else	parent.appendChild(NormalLine);
*/
//				style.fill.fill = style.stroke.stroke;
				//** fuku 2002/04/17 **
				if ( ((GLine)item).isArrow(GLine.ARROW_FORWARD) ||
				     ((GLine)item).isArrow(GLine.ARROW_FORWARD) ) {
					
					Element gelem = doc.createElement("g");
					gelem.appendChild(NormalLine);

					// 矢印の塗りつぶし
					if ( ((GLine)item).isArrow(GLine.ARROW_FORWARD) ) {
						ZPath zpath = new ZPath(ZGraphicUtil.createArrowShape(p1,p2,8,4));
						SVGPathdata pathdata = converttoPathdata(zpath);
						gelem.appendChild(createPath(pathdata ,style));
					}
					if ( ((GLine)item).isArrow(GLine.ARROW_BACKWARD) ) {
						ZPath zpath = new ZPath(ZGraphicUtil.createArrowShape(p2,p1,8,4));
						SVGPathdata pathdata = converttoPathdata(zpath);
						gelem.appendChild(createPath(pathdata ,style));
					}
					parent.appendChild(gelem);
				} else {
					parent.appendChild(NormalLine);
				}

			}
			
			// GElement.GPath
			// ZPath getPath();
			else if(item instanceof GPath){
				ZPath zpath = ((GPath)item).getPath();

				PTransform mytrans = item.getTransform();
				zpath = new ZPath( mytrans.transform( zpath ) );

				if ( trans != null ) {
					zpath = new ZPath( trans.transform( zpath ) );
				}

				SVGPathdata pathdata = converttoPathdata(zpath);

				parent.appendChild(createPath(pathdata ,style));
			}

	return bounds;
	}
	
	protected	SVGPathdata	converttoPathdata(ZPath zpath){
		byte[]		types	= zpath.getTypes();
		double[]	coords	= zpath.getCoords();
		
		SVGPathdata pathdata = new SVGPathdata();
		
		int cix	= 0;
		int mx	= zpath.getNumTypes();
		int cmx	= coords.length;
		for (int i=0;i<mx;i++){
			switch (types[i]){
			case (byte)PathIterator.SEG_MOVETO:
				if(cix+2 > cmx)	break;
				pathdata.moveto(coords[cix] ,coords[cix+1]);
				cix += 2;
				break;
			case (byte)PathIterator.SEG_LINETO:
				if(cix+2 > cmx)	break;
				pathdata.lineto(coords[cix] ,coords[cix+1]);
				cix += 2;
				break;
			case (byte)PathIterator.SEG_QUADTO:
				if(cix+4 > cmx)	break;
				pathdata.quadto(coords[cix  ] ,coords[cix+1]
							   ,coords[cix+2] ,coords[cix+3]);
				cix += 4;
				break;
			case (byte)PathIterator.SEG_CUBICTO:
				if(cix+6 > cmx)	break;
				pathdata.curveto(coords[cix  ] ,coords[cix+1]
								,coords[cix+2] ,coords[cix+3]
								,coords[cix+4] ,coords[cix+5]);
				cix += 6;
				break;
			case (byte)PathIterator.SEG_CLOSE:
				pathdata.closepath();
				break;
		    }
		}
		
		return(pathdata);
	}
	
	public void setLogo(Element logo){
		if( !"svg".equals(logo.getTagName()) ) return;
		
		// svg の height属性の分だけキャンパスを上に伸ばす
		double height = 0;
		try{ height = Double.parseDouble(logo.getAttribute(HEIGHT)); }
		catch(Throwable t){}
		
		myRootY -= height;
		myRootH += height;
		setViewBox(myRootX ,myRootY ,myRootW ,myRootH);
		
		// <g transform="translate( .... , .... )">
		Element g = doc.createElement("g");
		g.setAttribute("transform", "translate("+String.valueOf(myRootX)+","+String.valueOf(myRootY)+")");
		
		// コピー
		NodeList nl	= logo.getChildNodes();
		if(null==nl)	return;
		
		int mx = nl.getLength();
		for(int i = 0; i < mx; i++){
			Node	node	=	nl.item(i);
			g.appendChild(doc.importNode(node, true));
		}
		
		// ルートに追加
		rootelement.appendChild(g);
	}
}