/*
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.*;

/**
 * A <code>Plane</code> object represents a mathematical plane in an arbitrary cartesian co-ordinate system. A
 * <code>Plane</code> is defined by a normal vector and a distance along that vector from the origin, where the distance
 * represents the distance from the origin to the <code>Plane</code> rather than from the <code>Plane</code> to the
 * origin.
 * <p/>
 * <p/>
 * Instances of <code>Plane</code> are immutable. </p>
 *
 * @author Tom Gaskins
 * @version $Id: Plane.java 1749 2007-05-06 19:48:14Z tgaskins $
 */
public final class Plane
{

    /**
     * Represents all the information about this <code>Plane</code>. The first three values (<code>x, y, z</code>) of
     * <code>v</code> represent a normal <code>Vector</code> to the <code>Plane</code>, while the fourth
     * (<code>w</code>) represents the signed distance this <code>Plane</code> has been shifted along that normal.
     */
//    private final Vector v;
    private final Point n;

    /**
     * Obtains a new instance of a <code>Plane</code> whose information is contained in <code>Vector</code>
     * <code>vec</code>.
     *
     * @param vec the <code>Vector</code> containing information about this <code>Plane</code>'s normal and distance
     * @throws IllegalArgumentException if passed a null or zero-length <code>Vector</code>
     */
    public Plane(Point vec)
    {
        if (vec == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.VectorIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        if (vec.selfDot() == 0)
        {
            String message = WorldWind.retrieveErrMsg("geom.Plane.VectorIsZero");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        this.n = vec;
    }

    /**
     * Obtains a new <code>Plane</code> whose normal is defined by the vector (a,b,c) and whose disance from that vector
     * is d. The vector may not have zero length.
     *
     * @param a the x-parameter of the normal to this <code>Plane</code>
     * @param b the y-parameter of the normal to this <code>Plane</code>
     * @param c the z-parameter of the normal to this <code>Plane</code>
     * @param d the distance of this <code>Plane</code> from the origin along its normal.
     * @throws IllegalArgumentException if <code>0==a==b==c</code>
     */
    public Plane(double a, double b, double c, double d)
    {
        if (a == 0 && b == 0 && c == 0)
        {
            String message = WorldWind.retrieveErrMsg("geom.Plane.VectorIsZero");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        this.n = new Point(a, b, c, d);
    }

    /**
     * Retrieves a <code>Point</code> representing the normal to this <code>Plane</code>.
     *
     * @return a <code>Point</code> representing the normal to this <code>Plane</code>
     */
    public final Point getNormal()
    {
        return new Point(this.n.x(), this.n.y(), this.n.z());
    }

    /**
     * Retrieves the distance from the origin to this <code>Plane</code>. Two options exist for defining distance - the
     * first represents the distance from the origin to the <code>Plane</code>, the second represents the distance from
     * the <code>Plane</code> to the origin. This function uses the first method. The outcome of this is that depending
     * on the caller's view of this method, the sign of distances may appear to be reversed.
     *
     * @return the distance between this <code>Plane</code> and the origin
     */
    public final double getDistance()
    {
        return this.n.w();
    }

    /**
     * Retrieves a vector representing the normal and distance to this <code>Plane</code>. The
     * vector has the structure (x, y, z, distance), where (x, y, z) represents the normal, and distance
     * represents the distance from the origin.
     *
     * @return a <code>Vector</code> representation of this <code>Plane</code>
     */
    public final Point getVector()
    {
        return this.n;
    }

    /**
     * Calculates the dot product of this <code>Plane</code> with Point <code>p</code>.
     *
     * @param p the Point to dot with this <code>Plane</code>
     * @return the dot product of <code>p</code> and this <code>Plane</code>
     * @throws IllegalArgumentException if <code>p</code> is null
     */
    public final double dot(Point p)
    {
        if (p == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        return this.n.x() * p.x() + this.n.y() * p.y() + this.n.z() * p.z() + this.n.w() * p.w();
    }

    @Override
    public final String toString()
    {
        return this.n.toString();
    }

    @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.Plane plane = (gov.nasa.worldwind.geom.Plane) o;

        //noinspection RedundantIfStatement
        if (!this.n.normalize().equals(plane.n.normalize()))
            return false;

        return true;
    }

    @Override
    public final int hashCode()
    {
        return this.n.hashCode();
    }
}