#if !defined(PS_POSITION_H)
#define PS_POSITION_H

/** \file psPosition.h
 *  \brief Definitions of objects in 2-D space 
 *  \ingroup AstroGroup
 */

/** Structures *********************/

/** projection types */
typedef enum {                          ///< type of val is:
    PS_PROJ_TAN,                        ///< Tangent projection
    PS_PROJ_SIN,                        ///< Sine projection
    PS_PROJ_AIT,                        ///< Aitoff projection
    PS_PROJ_PAR,                        ///< Par projection
    PS_PROJ_NTYPE                       ///< Number of types; must be last
} psProjectionType;

/** A point in 2-D space, with errors. */
typedef struct {
    double x;				///< x position
    double y;				///< y position
    double xErr;			///< Error in x position
    double yErr;			///< Error in y position
} psPlane;

/** A point on the surface of a sphere, with errors */
typedef struct {
    double r;				///< RA
    double d;				///< Dec
    double rErr;			///< Error in RA
    double dErr;			///< Error in Dec
} psSphere;

/** A polynomial transformation between coordinate frames.  This may be a linear relationship, or may
 *  represent a higher-order transformation.
 */
typedef struct {
    psDPolynomial2D *x;			///< Transformation for x
    psDPolynomial2D *y;			///< Transformation for y
} psPlaneTransform;

/** The optical distortion terms.  The lowest two terms are the x and y axis of the target system.  The higher
 *  two terms represent magnitude and color terms.
 */
typedef struct {
    psDPolynomial4D *x;			///< Transformation for x
    psDPolynomial4D *y;			///< Transformation for y
} psPlaneDistort;

/** General spherical transformation */
typedef struct {
  double sinNPlon;                       ///< sin of North Pole longitude
  double cosNPlon;                       ///< cos of North Pole longitude
  double sinNPlat;                       ///< sin of North Pole lattitude
  double cosNPlat;                       ///< cos of North Pole lattitude
  double sinZP;	                         ///< sin of First PT of Ares lon
  double cosZP;	                         ///< cos of First PT of Ares lon
} psSphereTransform;

/** Spherical <-> Linear projections */
typedef struct {
    double R, D;                         ///< coordinates of projection center
    double Xs, Ys;                       ///< plate-scale in X and Y directions
    psProjectionType type;               ///< projection type
} psProjection;

typedef enum {
    PS_SPHERICAL;			///< Offset on a sphere
    PS_LINEAR;				///< Linear offset
} psSphereOffsetMode;

typedef enum {
    PS_ARCSEC;				///< Arcseconds
    PS_ARCMIN;				///< Arcminutes
    PS_DEGREE;				///< Degrees
    PS_RADIAN;				///< Radians
} psSphereOffsetUnit;

/** Functions **************************************************************/
/** \addtogroup AstroGroup Astronomy-Specific Utilities
 *  \{
 */

/** apply the coordinate transformation to the given coordinate */
psPlane *psPlaneTransformApply (psPlane *out, ///< Output coordinates, or NULL
				const psPlaneTransform *transform, ///< coordinate transformation
				const psPlane *coords) ///< input coordiate
;

/** apply the optical distortion to the given coordinate, magnitude, color */
psPlane *psPlaneDistortApply (psPlane *out, ///< Output coordinates, or NULL
			      const psPlaneDistort *distort, ///< optical distortion pattern
			      const psPlane *coords, ///< input coordinate
			      float mag, ///< magnitude of object
			      float color) ///< color of object
;

/* Celestial coordinate conversions */

/** Constructor */
psSphereTransform *
psSphereTransformAlloc(double NPlon, ///< Longitude in input system of north pole
		       double NPlat, ///< Latitude in input system of north pole
		       double ZP) ///< Zero point (e.g, the first point of Ares)
;

/** Apply general spherical transformation */
psSphere *
psSphereTransformApply(psSphere *out,	///< Position to output, or NULL
		       const psSphereTransform *transform ///< transformation to use
		       const psSphere *coord) ///< Coordinates to convert
;

/** Return transformation structure to convert ICRS to Ecliptic */
psSphereTransform *psSphereTransformICRSToEcliptic(void);

/** Return transformation structure to convert Ecliptic to ICRS */
psSphereTransform *psSphereTransformEclipticToICRS(void);

/** Return transformation structure to convert ICRS to Galactic */
psSphereTransform *psSphereTransformICRSToGalactic(void);

/** Return transformation structure to convert Galactic to ICRS */
psSphereTransform *psSphereTransformGalacticToICRS(void);

/***********************************************************************************************************/


/** Project spherical system onto a plane */
psPlane *
psProject(const psSphere *coord, ///< Spherical coordinates to project
	  const psProjection *projection) ///< Projection to use
;

/** Deproject plane onto spherical system */
psSphere *
psDeproject(const psPlane *coord, ///< Plane coordinates to deproject
	    const psProjection *projection) ///< Projection to use
;

/** Get offset (RA,Dec) on the sky between two positions position1 and position2 may not be identical */
psSphere *
psSphereGetOffset(const psSphere *position1, ///< Position 1
		  const psSphere *position2, ///< Position 2
		  psSphereOffsetMode mode, ///< Mode of offset
		  psSphereOffsetUnit unit ///< Units for offset
;

/** Apply an offset to a position */
psSphere *
psSphereSetOffset(const psSphere *position, ///< Position
		  const psSphere *offset, ///< Offset
		  psSphereOffsetMode mode, ///< Mode of offset
		  psSphereOffsetUnit unit ///< Units of offset
    );

/************************************************************************************************************/

/* Positions of well-known objects */

/** These are not up to date: we don't need them immediately */

/** Get Sun Position */
psSphere *
psSunGetPos(psTime *time)		///< Time for which to get position
;

/** Get Sun Rise time */
psSphere *
psSunGetRise(psTime *twi15,		///< corresponding 15 deg twilight
	     psTime *twi18,		///< corresponding 18 deg twilight
	     const psTime *time)	///< get rise closest to this time
;

/** Get Sun Set time */
psSphere *
psSunGetSet(psTime *twi15,		///< corresponding 15 deg twilight
	    psTime *twi18,		///< corresponding 18 deg twilight
	    const psTime time)		///< get set closest to this time
;

/** Get Length of closest night in hours */
float
psNightLength(const psTime *time)	///< Time that specifies the night of interest
;

/** Get Moon Position */
psSphere *
psMoonGetPos(const psTime *time)	///< Time for which to get position
;

/** Get Moon Rise time and return position */
psSphere *
psMoonGetRise(psTime *time)		///< get rise closest to this time
;

/** Get Moon Set time and return position */
psSphere *
psMoonGetSet(psTime *time)		///< get set closest to this time, and output
;

/** Get Moon phase */
float
psMoonGetPhase(const psTime *time)	///< Get phase for this time
;

/** Get Planet positions */
psSphere *
psPlanetGetPos(const char *solarSystemObject, ///< Named S.S. object
	       const psTime *time)	///< Get position for this time
;

/***********************************************************************************************************/

/* \} */ // End of AstroGroup Functions

#endif
