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

import gov.nasa.worldwind.geom.*;

/**
 * The <code>View</code> interface provides basic methods for implementations to communicate state and state chages to
 * the caller. <code>Views</code> provide a coordinate transformation from the object coordinates of the model to eye
 * coordinates, which follow the OpenGL convention of a left-handed coordinate system with origin at the eye point and
 * looking down the negative-z axis.
 * <p/>
 * View implementations are free to constrain position and orientation, as well as supply any projection from eye to
 * clip coordinates they see fit. Therefore calls to {@link #setFieldOfView}, {@link #goToCoordinate}, {@link
 * #goToLatLon} or {@link #goToAltitude} are not guaranteed to produce the requested transformation. When exact
 * knowledge of these values in necessary, calling {@link #getFieldOfView}, {@link #getPosition} or {@link #getAltitude}
 * immediately after a call to {@link #apply} guarantees the value to be synchronized with data structures used for view
 * transformation (e.g. model-view and projection matrices).
 * <p/>
 * Views contain both fixed state and computed state. The computed state is typically updated during a call to the
 * {@link #apply} method. Most accessor methods in this interface return the computed state that was set during the most
 * recent call to <code>apply()</code>.
 *
 * @author Paul Collins
 * @version $Id: View.java 1795 2007-05-08 22:08:29Z dcollins $
 * @see gov.nasa.worldwind.geom.Frustum
 * @see gov.nasa.worldwind.geom.ViewFrustum
 */
public interface View extends WWObject
{
    /**
     * Calculates and applies <code>View's</code> internal state to the graphics context in <code>DrawContext</code>.
     * All subsequently rendered objects use this new state. Upon return, the OpenGL graphics context reflects the
     * values of this view, as do any computed values of the view, such as the model-view matrix and
     * <code>Frustum</code>.
     *
     * @param dc the current World Wind drawing context on which <code>View's</code> state will apply.
     * @throws IllegalArgumentException if <code>dc</code> is null, or if the <code>Globe</code> or <code>GL</code>
     *                                  contained in <code>dc</code> is null.
     */
    void apply(DrawContext dc);

    /**
     * Returns the 'model-view' matrix computed in <code>apply()</code>, which transforms model coordinates to eye
     * coordinates (where the eye is located at the origin, facing down the negative-z axis). This matrix is constructed
     * using the model space translation and orientation specific to each implementation of <code>View</code>.
     *
     * @return the current model-view matrix.
     */
    Matrix4 getModelViewMatrix();

    /**
     * Returns the 'projection' matrix computed in <code>apply()</code>, which transforms eye coordinates to homogeneous
     * clip coordinates (a box of dimension (2, 2, 2) centered at the origin). This matrix is constructed using the
     * projection parameters specific to each implementation of <code>View</code> (e.g. field-of-view, clipping plane
     * distances). The {@link #getFrustum} method returns the planes corresponding to this matrix.
     *
     * @return the current projection matrix.
     */
    Matrix4 getProjectionMatrix();

    /**
     * Returns a Rectangle representing the window bounds (x, y, width, height) of the viewport, computed in
     * <code>apply()</code>. Implementations of <code>View</code> will configure themselves to render in this viewport.
     *
     * @return the current window bounds of the viewport, or null if none exists.
     */
    java.awt.Rectangle getViewport();

    /**
     * Returns the viewing <code>Frustum</code> in eye coordinates, computed in <code>apply()</code>. The
     * <code>Frustum</code> is the portion of viewable space defined by three sets of parallel 'clipping' planes. The
     * method {@link #getFrustumInModelCoordinates} maintains the shape of this <code>Frustum</code>, but it has been
     * translated and aligned with the eye in model space.
     *
     * @return the current viewing frustum in eye coordinates.
     */
    Frustum getFrustum();

    /**
     * Returns the viewing <code>Frustum</code> transformed to model coordinates. Model coordinate frustums are useful
     * for performing multiple intersection tests in model coordinates.
     *
     * @return the current viewing frustum in model coordinates.
     */
    Frustum getFrustumInModelCoordinates();

    /**
     * Returns the horizontal field-of-view angle (the angle of visibility) associated with this <code>View</code>, or
     * null if the <code>View</code> implementation does not support a field-of-view.
     *
     * @return horizontal field-of-view angle, or null if none exists.
     */
    Angle getFieldOfView();

    /**
     * Sets the horiziontal field-of-view angle (the angle of visibillity) associated with this <code>View</code>. This
     * call may be ignored by implementations that do not support a field-of-view.
     *
     * @param newFov the new horizontal field-of-view angle.
     * @throws IllegalArgumentException if <code>newFov</code> is null.
     */
    void setFieldOfView(Angle newFov);

    /**
     * Generates a new coordinate system in which the <code>View</code> does not move, and model coordinates are reverse
     * transformed into eye coordinates. The origin for these coordinates will be <code>referenceCenter</code>,
     * therefore all objects drawn after a call to <code>pushReferenceCenter</code> should be with respect to this
     * <code>Point</code>, rather than the customary origin (0, 0, 0). This creates a new model-view matrix, which is
     * placed on the top of a matrix stack, and immediately applied to the current OpenGL context. In order to return to
     * the original coordinate space, callers should invoke {@link #popReferenceCenter} after rendering is complete.
     * Note that calls to {@link #getModelViewMatrix} will not return reference-center model-view matrix, but the
     * original matrix.
     *
     * @param dc              the current World Wind drawing context on which <code>View's</code> state will apply.
     * @param referenceCenter the <code>Point</code> to become the new model space origin.
     * @throws IllegalArgumentException if <code>referenceCenter</code> is null.
     */
    void pushReferenceCenter(DrawContext dc, Point referenceCenter);

    /**
     * Removes the model-view matrix on top of the matrix stack, and restores the matrix now on top. This has the effect
     * of immediately replacing the current OpenGL model-view matrix with the matrix below the top. When the stack size
     * is one, therefore containing the original model-view matrix computed by <code>apply()</code>, this method will
     * throw an exception.
     *
     * @param dc the current World Wind drawing context on which <code>View's</code> state will apply.
     * @throws IllegalStateException if the the refernce matrix stack is empty.
     */
    void popReferenceCenter(DrawContext dc);

    /**
     * Returns the eye position in model coordinates.
     *
     * @return the eye position in model coordinates.
     */
    Point getEyePoint();

    /**
     * Returns the <code>View</code> y-axis orientation in model coordinates.
     *
     * @return the y-axis vector in model coordinates.
     */
    Point getUpVector();

    /**
     * Returns the <code>View</code> z-axis orientation in model coordinates.
     *
     * @return the z-axis vector in model coordinates.
     */
    Point getForwardVector();

    /**
     * Moves the <code>View</code> eye point to the new polar coordinate (latitude, longitude, elevation).
     *
     * @param newLatLon   the new latitude and longitude of the eye point.
     * @param newAltitude the new eye altitude (in meters) above the surface,
     * @throws IllegalArgumentException if <code>newLatlon</code> is null.
     */
    void goToCoordinate(LatLon newLatLon, double newAltitude);

    /**
     * Returns the geographic (latitude, longitude, elevation) coordinate of the <code>View's</code> eye point. Latitude
     * and longitude represent the coordinate directly beneath (or above) the <code>View</code>, while elevation
     * represents the <code>View</code> altitude above the analytical <code>Globe</code> radius.
     *
     * @return the latitude and longitude coordinates of the eye point.
     */
    Position getPosition();

    /**
     * Moves the <code>View</code> eye point to the new geographic (latitude, longitude) coordinate. Altitude is left
     * unchanged.
     *
     * @param newLatLon the new latitude and longitude of the eye point.
     * @throws IllegalArgumentException if <code>newLatlon</code> is null.
     */
    void goToLatLon(LatLon newLatLon);

    /**
     * Returns the <code>View</code> eye altitude (in meters) above the last rendered <code>SectorGeometry</code>, or
     * the analytical <code>Globe</code>, depending on the implementation.
     *
     * @return the <code>View's</code> altitude (in meters) above the surface.
     */
    double getAltitude();

    /**
     * Moves the <code>View</code> eye point to the new altitude (in meters) above the last rendered
     * <code>SectorGeometry</code>, or the analytical <code>Globe</code>, depending on the implementation.
     *
     * @param newAltitude the new eye altitude (in meters) above the surface,
     */
    void goToAltitude(double newAltitude);

    /**
     * Returns the <code>View's</code> angle from true North.
     *
     * @return the angle from true North.
     */
    Angle getHeading();

    /**
     * Sets the <code>View's</code> angle to true North.
     *
     * @param newHeading the new angle to true North.
     * @throws IllegalArgumentException if <code>newHeading</code> is null.
     */
    void setHeading(Angle newHeading);

    /**
     * Returns the <code>View's</code> angle from the plane tangent to the surface.
     *
     * @return the angle from the surface tangent plane.
     */
    Angle getPitch();

    /**
     * Sets the <code>View's</code> angle to the plane tangent to the surface.
     *
     * @param newPitch the new angle to the surface tangent plane.
     * @throws IllegalArgumentException if <code>newPitch</code> is null.
     */
    void setPitch(Angle newPitch);

    /**
     * Returns a two-dimensional array containing the range of angles (inclusive) the <code>View</code> may limit
     * its pitch to, if pitch constraints are enabled.
     *
     * @return a two-dimensional array, with the minimum and maximum pitch angles.
     */
    Angle[] getPitchConstraints();

    /**
     * Sets the range of angles (inclusive) the <code>View</code> may limit its pitch to, if pitch constraints are
     * enabled.
     *
     * @param newMinPitch the minimum pitch angle.
     * @param newMaxPitch the maximum pitch angle.
     */
    void setPitchConstraints(Angle newMinPitch, Angle newMaxPitch);

    /**
     * Returns true when pitch constraints are enabled.
     *
     * @return true when pitch constraints are enabled.
     */
    boolean isEnablePitchConstraints();

    /**
     * Enable or disable pitch constraints.
     *
     * @param enabled true when pitch constraints should be enabled, false otherwise.
     */
    void setEnablePitchConstraints(boolean enabled);

    /**
     * Returns the <code>View's</code> angle about its local z-axis.
     *
     * @return the angle about the local z-axis.
     */
    Angle getRoll();

    /**
     * Sets the <code>View's</code> angle about its local z-axis.
     *
     * @param newRoll the new angle about the local z-axis.
     * @throws IllegalArgumentException if <code>newRoll</code> is null.
     */
    void setRoll(Angle newRoll);

    /**
     * Returns the <code>View's</code> translation in its forward direction.
     *
     * @return translation along the forward direction.
     */
    double getZoom();

    /**
     * Sets the <code>View's</code> translation in its forward direction.
     *
     * @param newZoom translation along the forward direction.
     */
    void setZoom(double newZoom);

    /**
     * Returns a two-dimensional array containing the range of values (inclusive) the <code>View</code> may limit
     * its zoom to, if zoom constraints are enabled.
     *
     * @return two-dimensional array, with the minimum and maximum zoom values.
     */
    double[] getZoomConstraints();

    /**
     * Sets the range of values (inclusive) the <code>View</code> may limit its zoom to, if zoom constraints are
     * enabled.
     *
     * @param newMinZoom the minimum zoom value.
     * @param newMaxZoom the maximum zoom value.
     */
    void setZoomConstraints(double newMinZoom, double newMaxZoom);

    /**
     * Returns true when zoom constraints are enabled.
     *
     * @return true when zoom constraints are enabled, false otherwise.
     */
    boolean isEnableZoomConstraints();

    /**
     * Enable or disable zoom constraints.
     *
     * @param enabled trhe when zoom constraints should be enabled, false otherwise.
     */
    void setEnableZoomConstraints(boolean enabled);

    /**
     * Computes a line, in model coordinates, originating from the eye point, and passing throught the point contained
     * by (x, y) on the <code>View's</code> projection plane (or after projection into model space).
     *
     * @param x the horizontal coordinate originating from the left side of <code>View's</code> projection plane.
     * @param y the vertical coordinate originating from the top of <code>View's</code> projection plane.
     * @return a line beginning at the <code>View's</code> eye point and passing throught (x, y) transformed into model
     *         space.
     */
    Line computeRayFromScreenPoint(double x, double y);

    /**
     * Computes the intersection of a line originating from the eye point (passing throught (x, y)) with the last
     * rendered <code>SectorGeometry</code>, or the last analytical <code>Globe</code> if no rendered geometry exists.
     *
     * @param x the horizontal coordinate originating from the left side of <code>View's</code> projection plane.
     * @param y the vertical coordinate originating from the top of <code>View's</code> projection plane.
     * @return the point on the surface in polar coordiantes.
     */
    Position computePositionFromScreenPoint(double x, double y);

    /**
     * Computes the screen-aligned dimension (in meters) that a screen pixel would cover at a given distance (also in
     * meters). This computation assumes that pixels dimensions are square, and therefore returns a single dimension.
     *
     * @param distance the distance from the eye point, in eye coordinates, along the z-axis. This value must be
     *                 positive but is otherwise unbounded.
     * @return the dimension of a pixel (in meters) at the given distance.
     * @throws IllegalArgumentException if <code>distance</code> is negative.
     */
    double computePixelSizeAtDistance(double distance);

    /**
     * Returns the distance from the <code>View's</code> eye point to the horizon point on the last rendered
     * <code>Globe</code>.
     *
     * @return the distance from the eye point to the horizon (in meters).
     */
    double computeHorizonDistance();

    /**
     * Maps a <code>Point</code> in model (cartesian) coordinates to a <code>Point</code> in screen coordinates. The
     * returned x and y are relative to the lower left hand screen corner, while z is the screen depth-coordinate. If
     * the model point cannot be sucessfully mapped, this will return null.
     *
     * @param modelPoint the model coordinate <code>Point</code> to project.
     * @return the mapped screen coordinate <code>Point</code>.
     * @throws IllegalArgumentException if <code>modelPoint</code> is null.
     */
    Point project(Point modelPoint);

    /**
     * Maps a <code>Point</code> in screen coordinates to a <code>Point</code> in model coordinates. The input x and y
     * are  relative to the lower left hand screen corner, while z is the screen depth-coordinate.  If the screen point
     * cannot be sucessfully mapped, this will return null.
     *
     * @param windowPoint the window coordinate <code>Point</code> to project.
     * @return the mapped screen coordinate <code>Point</code>.
     * @throws IllegalArgumentException if <code>windowPoint</code> is null.
     */
    Point unProject(Point windowPoint);
}
