//# TwoSidedShape.h:
//# Copyright (C) 1998,1999,2000,2002
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//#
//# This library is distributed in the hope that it will be useful, but WITHOUT
//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: aips2-request@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA
//#
//#
//# $Id: TwoSidedShape.h 21130 2011-10-18 07:39:05Z gervandiepen $

#ifndef COMPONENTS_TWOSIDEDSHAPE_H
#define COMPONENTS_TWOSIDEDSHAPE_H

#include <casacore/casa/aips.h>
#include <components/ComponentModels/ComponentShape.h>
#include <components/ComponentModels/ComponentType.h>
#include <casacore/casa/BasicSL/Complex.h>
#include <casacore/casa/Quanta/Unit.h>
#include <casacore/casa/Quanta/Quantum.h>
#include <casacore/casa/Arrays/ArrayFwd.h>

namespace casacore{

class DirectionCoordinate;
class MDirection;
class MVAngle;
class RecordInterface;
class String;

}

namespace casa { //# NAMESPACE CASA - BEGIN


// <summary>Base class for component shapes with two sides</summary>

// <use visibility=export> 

// <reviewed reviewer="" date="yyyy/mm/dd" tests="tTwoSidedShape" demos="dTwoSidedShape">
// </reviewed>

// <prerequisite>
//   <li> <linkto class=ComponentShape>ComponentShape</linkto>
//   <li> <linkto class=casacore::Quantum>Quantum</linkto>
// </prerequisite>

// <synopsis> 

// This base class adds a common interface for component shapes that have two
// sides. These shapes can be parameterised as having a major-axis width,
// minor-axis width and a position angle (as well as the reference direction).
// Currently there are two shapes which use this parameterisation, the 
// <linkto class=GaussianShape>Gaussian</linkto> shape and the 
// <linkto class=DiskShape>disk</linkto> shape. Shapes which do not have this
// parameterisation, such as the <linkto class=PointShape>point</linkto> shape,
// are derived directly from the 
// <linkto class=ComponentShape>ComponentShape</linkto> class.

// Functions in this class implement a more convenient way of setting the
// parameters associated with two-sided shapes than the very flexible, but
// clumsy, <src>parameters</src> functions. The width parameters can be set
// using the <src>setWidth</src> functions. These functions enforce the rule
// that the major-axis width must not be smaller than the minor-axis
// width. Hence the axial ratio, which is the minor-axis width divided by the
// major-axis width, must always be greater than zero and less than or equal to
// one.

// The functions in this class also free derived classes from the having to
// deal with units. Any angular unit can be used to specify the width of the
// shape. The widths returned by the majoraxis, minoraxis & the positionAngle
// functions are in the same units as they where specified in, unless the
// convertUnit function has been used to specify another unit.

// For maximum speed the <src>*InRad</src> functions are provided. These
// functions bypass all the computation involving units.

// The toRecord and fromRecord functions in this class standardise the record
// representation of classes derived from this one. In addition to the type and
// direction fields all records, discussed in the 
// <linkto class=ComponentShape>ComponentShape</linkto> class, there are three
// more mandatory fields called; majoraxis, minoraxis & positionangle. These
// fields are record representation of casacore::Quantum<casacore::Double> objects and hence
// contain a value and unit. eg., A typical record for any of the shapes
// derived from this class might be:
// <srcblock>
// c := [type = 'gaussian',
//       direction = [type = 'direction',
//                    refer = 'b1950',
//                    m0 = [value = .1, unit = 'rad']
//                    m1 = [value = -.1, unit = 'rad']
//                   ],
//       majoraxis = [value=.5, unit='arcmin'],
//       minoraxis = [value=1, unit='arcsec'],
//       positionangle = [value=10, unit='deg']
//      ]
// </srcblock>
// </synopsis>
//
// <example>

// Because this is an abstract base class, an actual instance of this class
// cannot be constructed. However the interface it defines can be used inside a
// function. This allows functions which have TwoSidedShapes as arguments to
// work for any derived class. Hence the printShape function shown below will
// work for GaussianShapes and DiskShapes but not for PointShapes.

// In this example the printShape function prints out the type of model it is
// working with, the reference direction of that model, the major-axis width,
// minor-axis width and the position angle. This example is also available in
// the <src>dTwoSidedShape.cc</src> file.
// <srcblock>
//  void printShape(const TwoSidedShape& theShape) {
//    cout << "This is a " << ComponentType::name(theShape.type())
//         << " shape " << endl 
//         << "with a reference direction of "
//         << theShape.refDirection().getAngle("deg") << " ("
//         << theShape.refDirection().getRefString() << ")" << endl
//         << "and a major axis of " << theShape.majorAxis() << endl
//         << "      minor axis of " << theShape.minorAxis() << endl
//         << "and   position angle of " << theShape.positionAngle() << endl;
//  }
// </srcblock>
// </example>
//

// <motivation>
// This base class was created to allow the reuse of a number of common
// functions by all derived classes.
// </motivation>

// <todo asof="1999/110/12">
//   <li> Use Measures & Quanta in the interface to the visibility functions.
// </todo>

// <linkfrom anchor="TwoSidedShape" classes="ComponentShape">
//  <here>TwoSidedShape</here> - a base class for shapes with two sides
// </linkfrom>

class TwoSidedShape: public ComponentShape
{
public:

  // a virtual destructor is needed so that the actual destructor in the
  // derived class will be used.
  virtual ~TwoSidedShape();

  // return the actual component type.
  virtual ComponentType::Shape type() const = 0;

  // set/get the width and orientation of the Shape. The width of the major
  // must be larger than the width of the minor axes. The position angle is
  // measured North through East ie a position angle of zero degrees means
  // that the major axis is North-South and a position angle of 10 degrees
  // moves the Northern edge to the East. The axial ratio is the ratio of the
  // minor to major axes widths. Hence it is always between zero and one.
  // <group>
  void setWidth(const casacore::Quantum<casacore::Double>& majorAxis,
		const casacore::Quantum<casacore::Double>& minorAxis, 
		const casacore::Quantum<casacore::Double>& positionAngle);
  void setWidth(const casacore::Quantum<casacore::Double>& majorAxis,
		const casacore::Double axialRatio, 
		const casacore::Quantum<casacore::Double>& positionAngle);
  casacore::Quantum<casacore::Double> majorAxis() const;
  casacore::Quantum<casacore::Double> minorAxis() const;
  casacore::Quantum<casacore::Double> positionAngle() const;
  casacore::Double axialRatio() const;
  // </group>

  // set/get the errors on the shape parameters. 
  // <group>
  void setErrors(const casacore::Quantum<casacore::Double>& majorAxisError,
		 const casacore::Quantum<casacore::Double>& minorAxisError, 
		 const casacore::Quantum<casacore::Double>& positionAngleError);
  const casacore::Quantum<casacore::Double>& majorAxisError() const;
  const casacore::Quantum<casacore::Double>& minorAxisError() const;
  const casacore::Quantum<casacore::Double>& positionAngleError() const;
  casacore::Double axialRatioError() const;
  // </group>

  // set/get the width and orientation of the Shape. These are the same as the
  // above functions except that all widths are in radians.
  // <group>
  virtual void setWidthInRad(const casacore::Double majorAxis,
			     const casacore::Double minorAxis, 
			     const casacore::Double positionAngle) = 0;
  virtual casacore::Double majorAxisInRad() const = 0;
  virtual casacore::Double minorAxisInRad() const = 0;
  virtual casacore::Double positionAngleInRad() const = 0;
  // </group>

  // Calculate the proportion of the flux that is in a pixel of specified size
  // centered in the specified direction. The returned value will always be
  // between zero and one (inclusive).
  virtual casacore::Double sample(const casacore::MDirection& direction, 
			const casacore::MVAngle& pixelLatSize,
			const casacore::MVAngle& pixelLongSize) const = 0;

  // Same as the previous function except that many directions can be sampled
  // at once. The reference frame and pixel size must be the same for all the
  // specified directions. A default implementation of this function is
  // available that uses the single pixel sample function described above.
  // However customised versions of this function will be more efficient as
  // intermediate values only need to be computed once.
  virtual void sample(casacore::Vector<casacore::Double>& scale, 
		      const casacore::Vector<casacore::MDirection::MVType>& directions, 
		      const casacore::MDirection::Ref& refFrame,
		      const casacore::MVAngle& pixelLatSize,
		      const casacore::MVAngle& pixelLongSize) const = 0;

  // Return the Fourier transform of the component at the specified point in
  // the spatial frequency domain. The point is specified by a 3-element vector
  // (u,v,w) that has units of meters and the frequency of the observation, in
  // Hertz. These two quantities can be used to derive the required spatial
  // frequency <src>(s = uvw*freq/c)</src>. The w component is not used in
  // these functions.  The scale factor returned by this function can be used
  // to scale the flux at the origin of the Fourier plane in order to determine
  // the visibility at the specified point.

  // The "origin" of the transform is the reference direction of the
  // component. This means for symmetric components, where the reference
  // direction is at the centre, that the Fourier transform will always be
  // real.
  virtual casacore::DComplex visibility(const casacore::Vector<casacore::Double>& uvw,
			      const casacore::Double& frequency) const = 0;

  // Same as the previous function except that many (u,v,w) points can be
  // sampled at once. The observation frequency is the same for all the
  // specified points. The uvw casacore::Matrix must have first dimension of three and
  // the second dimension must match the length of the scale vector. A default
  // implementation of this function is available that uses the single point
  // visibility function described above.  However customised versions of this
  // function may be more efficient as intermediate values only need to be
  // computed once.
  virtual void visibility(casacore::Vector<casacore::DComplex>& scale, const casacore::Matrix<casacore::Double>& uvw,
			  const casacore::Double& frequency) const = 0;

  // same as above but with many frequencies
 virtual void visibility(casacore::Matrix<casacore::DComplex>& scale, const casacore::Matrix<casacore::Double>& uvw,
			  const casacore::Vector<casacore::Double>& frequency) const = 0; 

  // determine whether the shape is symmetric or not. Always returns true.
  virtual casacore::Bool isSymmetric() const;

  // Return a pointer to a copy of this object upcast to a ComponentShape
  // object. The class that uses this function is responsible for deleting the
  // pointer. This is used to implement a virtual copy constructor.
  virtual ComponentShape* clone() const = 0;

  // set/get the shape parameters associated with this shape. There are always
  // three these being in order: the major-axis, the minor-axis and the
  // position angle. All these angular quantities are specified and returned in
  // radians. The casacore::Vector supplied to the <src>setParameters</src> and
  // <src>setErrors</src> functions must have three elements and the Vector
  // returned by the <src>parameters</src> and <src>errors</src> functions will
  // have three elements. The errors are nominally 1-sigma in an implicit
  // Gaussian distribution.
  // <group>
  virtual casacore::uInt nParameters() const;
  virtual void setParameters(const casacore::Vector<casacore::Double>& newParms);
  virtual casacore::Vector<casacore::Double> parameters() const;
  virtual void setErrors(const casacore::Vector<casacore::Double>& newParms);
  virtual casacore::Vector<casacore::Double> errors() const;
  virtual casacore::Vector<casacore::Double> optParameters() const;
  virtual void setOptParameters(const casacore::Vector<casacore::Double>& newOptParms);
  // </group>

  // This functions convert between a casacore::Record and a shape derived from this
  // class. These functions define how the object is represented in glish and
  // this is detailed in the synopsis above.  They return false if the record
  // is malformed and append an error message to the supplied string giving the
  // reason.
  // <group>
  virtual casacore::Bool fromRecord(casacore::String& errorMessage,
			  const casacore::RecordInterface& record);
  virtual casacore::Bool toRecord(casacore::String& errorMessage,
			casacore::RecordInterface& record) const;
  // </group>

  // Convert the parameters of the component to the specified units. The
  // supplied record must have three fields, namely 'majoraxis', 'minoraxis' &
  // 'positionangle'. These fields must contains strings that are angular units
  // and this function will convert the corresponding parameters to the
  // specified units. This will have no effect on the shape of this class but
  // will affect the format of the record returned by the toRecord function,
  // and the units used in the the Quanta returned by the majoraxis, minoraxis
  // & positionangle functions.
  virtual casacore::Bool convertUnit(casacore::String& errorMessage,
			   const casacore::RecordInterface& record);

  // casacore::Function which checks the internal data of this class for correct
  // dimensionality and consistent values. Returns true if everything is fine
  // otherwise returns false.
  virtual casacore::Bool ok() const;

  // Convert component shape to absolute pixels (longitude, latitude, major axis, 
  // minor axis, position angle [positive +x -> +y ; rad])
  virtual casacore::Vector<casacore::Double> toPixel (const casacore::DirectionCoordinate& dirCoord) const;

  // Fill the shape from the vector (longitude, latitude, major axis, 
  // minor axis, position angle [positive +x -> +y ; rad]).  The return
  // is true if the input major axis (in pixels) became the minor 
  // axis (in world coordinates), else false.
  virtual casacore::Bool fromPixel (const casacore::Vector<casacore::Double>& parameters,
                          const casacore::DirectionCoordinate& dirCoord);

  // Get the string containing the various size quantities of a component.
  virtual casacore::String sizeToString() const = 0;

  // casacore::Format the string containing the various size quantities of a component.
  static casacore::String sizeToString(
    	casacore::Quantity major, casacore::Quantity minor, casacore::Quantity posangle,
    	casacore::Bool includeUncertainties = true, casacore::Quantity majorErr = 0,
    	casacore::Quantity minorErr = 0, casacore::Quantity posanErr = 0);

protected:
  // The constructors and assignment operator are protected as only derived
  // classes should use them.
  // <group>
  //# The default TwoSidedShape is at the J2000 North Pole.
  TwoSidedShape();

  //# Construct a TwoSidedShape at the specified direction, and return all all
  //# widths using the specified units.
  TwoSidedShape(const casacore::MDirection& direction, const casacore::Unit& majorAxisUnit, const
		casacore::Unit& minorAxisUnit, const casacore::Unit& paUnit);

  //# The copy constructor uses copy semantics.
  TwoSidedShape(const TwoSidedShape& other);

  //# The assignment operator uses copy semantics.
  TwoSidedShape& operator=(const TwoSidedShape& other);
  // </group>

private:
  casacore::Unit itsMajUnit;
  casacore::Unit itsMinUnit;
  casacore::Unit itsPaUnit;
  casacore::Quantum<casacore::Double> itsMajErr;
  casacore::Quantum<casacore::Double> itsMinErr;
  casacore::Quantum<casacore::Double> itsPaErr;
//
  casacore::Vector<casacore::Double> widthToCartesian (const casacore::Quantum<casacore::Double>& width,
                                   const casacore::Quantum<casacore::Double>& pa, 
                                   const casacore::MDirection& dirRef,
                                   const casacore::DirectionCoordinate& dirCoord,
                                   const casacore::Vector<casacore::Double>& pixelCen) const;
//
  casacore::MDirection directionFromCartesian (casacore::Double width, casacore::Double pa,
                                     const casacore::DirectionCoordinate& dirCoord,
                                     const casacore::Vector<casacore::Double>& pixelCen) const;

};

} //# NAMESPACE CASA - END

#endif