/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.geom;

import gov.nasa.worldwind.*;

/**
 * @author Tom Gaskins
 * @version $Id: Line.java 1749 2007-05-06 19:48:14Z tgaskins $
 */
public final class Line// Instances are immutable
{
    private final Point origin;
    private final Point direction;

    /**
     * @param origin
     * @param direction
     * @throws IllegalArgumentException if <code>origin</code> is null, or <code>direction</code> is null or has zero
     *                                  length
     */
    public Line(Point origin, Point direction)
    {
        String message = null;
        if (origin == null)
            message = "nullValue.OriginIsNull";
        else if (direction == null)
            message = "nullValue.DirectionIsNull";
        else if (direction.length() <= 0)
            message = "geom.Line.DirectionIsZeroVector";
        if (message != null)
        {
            message = WorldWind.retrieveErrMsg(message);
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        this.origin = origin;
        this.direction = direction;
    }

    public final Point getDirection()
    {
        return direction;
    }

    public final Point getOrigin()
    {
        return origin;
    }

    public final Point getPointAt(double t)
    {
        return Point.fromOriginAndDirection(t, this.direction, this.origin);
    }

    public final double selfDot()
    {
        return this.origin.dot(this.direction);
    }

    /**
     * Performs a comparison to test whether this Object is internally identical to the other Object <code>o</code>.
     * This method takes into account both direction and origin, so two lines which may be equivalent may not be
     * considered equal.
     *
     * @param o the object to be compared against.
     * @return true if these two objects are equal, false otherwise
     */
    @Override
    public final boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        final gov.nasa.worldwind.geom.Line line = (gov.nasa.worldwind.geom.Line) o;

        if (!direction.equals(line.direction))
            return false;
        if (!line.origin.equals(origin))
            return false;

        return true;
    }

    @Override
    public final int hashCode()
    {
        int result;
        result = origin.hashCode();
        result = 29 * result + direction.hashCode();
        return result;
    }

    public String toString()
    {
        return "Origin: " + this.origin + ", Direction: " + this.direction;
    }

    /**
     * Calculate the shortests distance between this line and a specified <code>Point</code>. This method returns a
     * positive distance.
     *
     * @param p the <code>Point</code> whose distance from this <code>Line</code> will be calculated
     * @return the distance between this <code>Line</code> and the specified <code>Point</code>
     * @throws IllegalArgumentException if <code>p</code> is null
     */
    public final double distanceTo(Point p)
    {
        if (p == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        Point origin = this.getOrigin();
        Point sideB = origin.subtract(p); // really a vector

        double distanceToOrigin = sideB.dot(this.getDirection());
        double divisor = distanceToOrigin / this.getDirection().selfDot();

        Point sideA = this.getDirection().multiply(divisor);

        double aSquared = sideA.selfDot();
        double bSquared = sideB.selfDot();

        return Math.sqrt(bSquared - aSquared);
    }
}
