// PathVisio,
// a tool for data visualization and analysis using Biological Pathways
// Copyright 2006-2011 BiGCaT Bioinformatics
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package org.pathvisio.core.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;

import org.pathvisio.core.model.AnchorType;
import org.pathvisio.core.model.GraphLink.GraphRefContainer;
import org.pathvisio.core.model.PathwayElement.MAnchor;
import org.pathvisio.core.model.PathwayElement.MPoint;

/**
 * VAnchor is the view representation of {@link MAnchor}.
 *
 * It is stuck to a Line and can move one-dimensionally across it. It has a handle
 * so the user can drag it.
 */
public class VAnchor extends VPathwayElement implements LinkProvider, Adjustable {
	private MAnchor mAnchor;
	private Line line;
	private Handle handle;

	private double mx = Double.NaN;
	private double my = Double.NaN;

	public VAnchor(MAnchor mAnchor, Line parent) {
		super(parent.getDrawing());
		this.mAnchor = mAnchor;
		this.line = parent;
		updatePosition();
	}

	public double getVx() {
		return vFromM(mx);
	}

	public double getVy() {
		return vFromM(my);
	}

	public Handle getHandle() {
		return handle;
	}

	public MAnchor getMAnchor() {
		return mAnchor;
	}

	protected void destroy() {
		super.destroy();
		line.removeVAnchor(this);
	}

	@Override protected void destroyHandles()
	{
		if (handle != null)
		{
			handle.destroy();
			handle = null;
		}
	}

	protected void createHandles()
	{
		handle = new Handle(Handle.Freedom.FREE, this, this);
		double lc = mAnchor.getPosition();
		Point2D position = line.vFromL(lc);
		handle.setVLocation(position.getX(), position.getY());
	}

	void updatePosition() {
		double lc = mAnchor.getPosition();

		Point2D position = line.vFromL(lc);
		if (handle != null) handle.setVLocation(position.getX(), position.getY());

		mx = mFromV(position.getX());
		my = mFromV(position.getY());

		//Redraw graphRefs
		for(GraphRefContainer ref : mAnchor.getReferences()) {
			if(ref instanceof MPoint) {
				VPoint vp = canvas.getPoint((MPoint)ref);
				if(vp != null && vp.getLine() != line) {
					vp.getLine().recalculateConnector();
				}
			}
		}
	}

	public void adjustToHandle(Handle h, double vx, double vy)
	{
		double position = line.lFromV(new Point2D.Double(vx, vy));
		mAnchor.setPosition(position);
	}

	private AnchorShape getAnchorShape() {
		AnchorShape shape = ShapeRegistry.getAnchor(
				mAnchor.getShape().getName());

		if(shape != null)
		{
			AffineTransform f = new AffineTransform();
			double scaleFactor = vFromM (1.0);
			f.translate (getVx(), getVy());
			f.scale (scaleFactor, scaleFactor);
			Shape sh = f.createTransformedShape(shape.getShape());
			shape = new AnchorShape (sh);
		}
		return shape;
	}

	private Shape getShape() {
		AnchorShape shape = getAnchorShape();
		return shape != null ? shape.getShape() : handle.getVOutline();
	}

	protected void doDraw(Graphics2D g) {
        if (getMAnchor().getShape().equals(AnchorType.NONE) && getMAnchor().getGraphId() != null) {
            return;
        }
        Color c;

		if(isSelected()) {
			c = selectColor;
		}
		else {
			c = line.getPathwayElement().getColor();
		}

		AnchorShape arrowShape = getAnchorShape();
		if(arrowShape != null)
		{
			g.setStroke(new BasicStroke());
				g.setPaint (c);
				g.fill (arrowShape.getShape());
				g.draw(arrowShape.getShape());
		}

		if(isHighlighted()) {
			Color hc = getHighlightColor();
			g.setColor(new Color (hc.getRed(), hc.getGreen(), hc.getBlue(), 128));
			g.setStroke (new BasicStroke (HIGHLIGHT_STROKE_WIDTH));
			g.draw (getShape());
		}
	}

	//Minimum outline diameter of 15px
	static final double MIN_OUTLINE = 15;

	protected Shape calculateVOutline() {
		Shape s = getShape();
		Rectangle b = s.getBounds();
		//Create a larger shape if the given shape
		//is smaller than the minimum
		if(b.getWidth() < MIN_OUTLINE ||
			b.getHeight() < MIN_OUTLINE) {
			s = new Ellipse2D.Double(
					getVx() - MIN_OUTLINE / 2, getVy() - MIN_OUTLINE / 2,
					MIN_OUTLINE, MIN_OUTLINE
			);
		}
		return s;
	}

	LinkAnchor linkAnchor = null;

	public LinkAnchor getLinkAnchorAt(Point2D p) {
		if(linkAnchor != null && linkAnchor.getMatchArea().contains(p)) {
			return linkAnchor;
		}
		return null;
	}

	public void hideLinkAnchors() 
	{
		if (linkAnchor != null) linkAnchor.destroy();
		linkAnchor = null;
	}

	public void showLinkAnchors() 
	{
		linkAnchor = new LinkAnchor(canvas, this, mAnchor, 0, 0);
	}

	/**
	 * Returns the zorder of the parent line
	 */
	protected int getZOrder() {
		return line.getPathwayElement().getZOrder() + 1;
	}
	
}
