/*******************************************************************************
 * Copyright (c) 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.draw2d.examples.tree;

import java.util.List;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Layer;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.ToolbarLayout;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;

/**
 * @author hudsonr
 * Created on Apr 21, 2003
 */
public class TreeBranch extends Figure {

class AnimatingLayer extends Layer {

/**
 * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle)
 */
public void setBounds(Rectangle rect) {

	int x = bounds.x,
		y = bounds.y;

	boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height),
		  translate = (rect.x != x) || (rect.y != y);

	if (isVisible() && (resize || translate))
		erase();
	if (translate) {
		int dx = rect.x - x;
		int dy = rect.y - y;
		primTranslate(dx, dy);
	}
	bounds.width = rect.width;
	bounds.height = rect.height;
//	if (resize)  Layouts dont depend on size.
//		invalidate();
	if (resize || translate) {
		fireMoved();
		repaint();
	}
}

}

public static final int STYLE_HANGING = 1;
public static final int STYLE_NORMAL = 2;

int aligment = PositionConstants.CENTER;

/*
 * A layer is being used simply because it is the only "transparent" figure in draw2d. See
 * the implementation of Layer.containsPoint(...) for what is meant by "transparent". If a
 * layer is not used, then overlapping branches will cause hit-test problems.
 */
AnimatingLayer contents = new AnimatingLayer();
boolean expanded = true;

IFigure node;
int style;

public TreeBranch(IFigure title) {
	this(title, STYLE_NORMAL);
}

public TreeBranch(IFigure title, int style) {
	setStyle(style);
	if (title.getBorder() == null)
		title.setBorder(new LineBorder(ColorConstants.gray, 2));
	this.node = title;
	add(contents);
	add(title);
}

/**
 * recursively set all nodes and sub-treebranch nodes to the same location.  This gives
 * the appearance of all nodes coming from the same place.
 * @param bounds where to set
 */
public void animationReset(Rectangle bounds) {
	List subtrees = contents.getChildren();
	contents.setBounds(bounds);

	//Make the center of this node match the center of the given bounds
	Rectangle r = node.getBounds();
	int dx = bounds.x + bounds.width / 2 - r.x - r.width/2;
	int dy = bounds.y + bounds.height/ 2 - r.y - r.height/2;
	node.translate(dx, dy);
	revalidate(); //Otherwise, this branch will not layout
	
	//Pass the location to all children
	for (int i=0; i<subtrees.size(); i++){
		TreeBranch subtree = (TreeBranch)subtrees.get(i);
		subtree.setBounds(bounds);
		subtree.animationReset(bounds);
	}
}

public void collapse() {
	if (!expanded)
		return;

	IFigure root = this;
	Viewport port = null;
	Point viewportStart = null;
	while (root.getParent() != null) {
		if (root instanceof Viewport)
			port = ((Viewport)root);
		root = root.getParent();
	}
	viewportStart = port.getViewLocation();
	Point nodeStart = node.getBounds().getLocation();
	setExpanded(false);
	root.validate();
	
	setExpanded(true);
	animationReset(getNodeBounds());
	Animation.mark(getNode());
	Animation.captureLayout(getRoot());
	Animation.swap();
	Animation.trackLocation = nodeStart;

	root.validate();
	port.setViewLocation(viewportStart);
	while(Animation.step())
		getUpdateManager().performUpdate();
	Animation.end();
	setExpanded(false);
}

/**
 * @see org.eclipse.draw2d.Figure#containsPoint(int, int)
 */
public boolean containsPoint(int x, int y) {
	return node.containsPoint(x, y)
		|| contents.containsPoint(x, y);
}

public void expand() {
	if (expanded)
		return;
	setExpanded(true);
	animationReset(getNodeBounds());
	
	Animation.mark(getNode());
	Animation.captureLayout(getRoot());

	while(Animation.step())
		getUpdateManager().performUpdate();
	Animation.end();
}

public int getAlignment() {
	return aligment;
}

protected BranchLayout getBranchLayout() {
	return (BranchLayout)getLayoutManager();
}

public IFigure getContentsPane() {
	return contents;
}

public int[] getContourLeft() {
	return getBranchLayout().getContourLeft();
}

public int[] getContourRight() {
	return getBranchLayout().getContourRight();
}


public int getDepth() {
	return getBranchLayout().getDepth();
}

/**
 * @see org.eclipse.draw2d.Figure#getMinimumSize(int, int)
 */
public Dimension getMinimumSize(int wHint, int hHint) {
	if (!Animation.PLAYBACK)
		validate();
	return super.getMinimumSize(wHint, hHint);
}

public IFigure getNode() {
	return node;
}

public Rectangle getNodeBounds() {
	return node.getBounds();
}

public int[] getPreferredRowHeights() {
	return getBranchLayout().getPreferredRowHeights();
}

/**
 * @see org.eclipse.draw2d.Figure#getPreferredSize(int, int)
 */
public Dimension getPreferredSize(int wHint, int hHint) {
	if (!Animation.PLAYBACK)
		validate();
	return super.getPreferredSize(wHint, hHint);
}

public TreeRoot getRoot() {
	return ((TreeBranch)getParent().getParent()).getRoot();
}

public int getStyle() {
	return style;
}

/**
 * @return
 */
public boolean isExpanded() {
	return expanded;
}

/**
 * @see org.eclipse.draw2d.Figure#paintFigure(org.eclipse.draw2d.Graphics)
 */
protected void paintFigure(Graphics graphics) {
	super.paintFigure(graphics);
	if (isExpanded())
		getBranchLayout().paintLines(graphics);
//	if (getDepth() == 2)
//		graphics.drawRectangle(getBounds().getResized(-1, -1));
}

public void setAlignment(int value) {
	aligment = value;
	revalidate();
}

/**
 * @param b
 */
public void setExpanded(boolean b) {
	if (expanded == b)
		return;
	expanded = b;
	contents.setVisible(b);
	revalidate();
}

public void setNode(IFigure node) {
	remove(this.node);
	add(this.node, 0);
}

public void setRowHeights(int heights[], int offset) {
	getBranchLayout().setRowHeights(heights, offset);
}

public void setStyle(int style) {
	if (this.style == style)
		return;
	this.style = style;
	switch (style) {
		case STYLE_HANGING :
			setLayoutManager(new HangingLayout(this));
			break;

		default :
			setLayoutManager(new NormalLayout(this));
			contents.setLayoutManager(new TreeLayout());
			break;
	}
}

/**
 * @see java.lang.Object#toString()
 */
public String toString() {
	return toString(0);
}

public String toString(int level) {
	String result = "";
	for (int i=0; i<level; i++)
		result += "  ";
	result += getChildren().get(1) + "\n";
	for (int i=0; i<contents.getChildren().size(); i++)
		result += ((TreeBranch)contents.getChildren().get(i)).toString(level + 1);
	return result;
}

/**
 * @see org.eclipse.draw2d.Figure#validate()
 */
public void validate() {
	if (isValid())
		return;
	if (style == STYLE_HANGING) {
		ToolbarLayout layout = new ToolbarLayout(!getRoot().isHorizontal()) {
			public void layout(IFigure parent) {
				Animation.recordInitialState(parent);
				if (Animation.playbackState(parent))
					return;
				
				super.layout(parent);
			}

		};
		layout.setMinorAlignment(ToolbarLayout.ALIGN_TOPLEFT);
		layout.setStretchMinorAxis(false);
		contents.setLayoutManager(layout);
	}
	repaint();
	super.validate();
}

}
