//# DiskShape.h:
//# Copyright (C) 1998,1999,2000
//# 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: DiskShape.h 21130 2011-10-18 07:39:05Z gervandiepen $

#ifndef COMPONENTS_DISKSHAPE_H
#define COMPONENTS_DISKSHAPE_H

#include <casacore/casa/aips.h>
#include <casacore/casa/BasicSL/Complex.h>
#include <components/ComponentModels/ComponentType.h>
#include <components/ComponentModels/TwoSidedShape.h>
#include <casacore/casa/Arrays/ArrayFwd.h>

namespace casacore{

class MDirection;
class MVAngle;
template <class Qtype> class Quantum;

}

namespace casa { //# NAMESPACE CASA - BEGIN


// <summary>A disk model for the spatial distribution of emission</summary>

// <use visibility=export> 

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

// <prerequisite>
//   <li> <linkto class=TwoSidedShape>TwoSidedShape</linkto>
// </prerequisite>

// <synopsis> 

// A DiskShape models the spatial distribution of radiation from the sky as a
// using a uniform brightness elliptical disk with user specified major axis
// width, minor axis width and position angle.

// This class like the other component shapes becomes more useful when used
// through the <linkto class=SkyComponent>SkyComponent</linkto> class, which
// incorperates the flux and spectral variation of the emission, or through the
// <linkto class=ComponentList>ComponentList</linkto> class, which handles
// groups of SkyComponent objects.

// The reference direction is defined in celestial co-ordinates, using a
// <linkto class=casacore::MDirection>MDirection</linkto> object. It indicates where the
// centre of the disk is on the sky. The direction can be specified both in
// the constructor or with the <src>setRefDirection</src> function.

// The width of the disk is defined as the angular diameter along the specified
// axis. The major axis has the larger width and is aligned North-South when
// the position angle is zero. A positive position angle moves the Northern
// side of the disk to the East.  The axial ratio is the ratio of the
// minor to major axis widths. The major axis MUST not be smaller than the
// minor axis otherwise an casacore::AipsError is thrown.

// These parameters of the disk (width, position angle, direction etc.) can be
// specified at construction time, using the <src>*inRad</src> functions or
// through functions, in the base classes. The base classes also implement
// functions for inter-converting this object into a record representation.

// The flux, or integrated intensity, is always normalised to one. This class
// does not model the actual flux or its variation with frequency. It solely
// models the way the emission varies with position on the sky.

// The <src>sample</src> member function is used to sample the component at any
// point on the sky. The scale factor calculated by this function is the
// proportion of the flux that is within a specified pixel size centered on the
// specified direction. This is not accurate for pixels which are partially
// covered by the disk. Ultimately this function will integrate the emission
// from the disk over the entire pixel but currently the returned flux will be
// either zero or a constant value with the returned value depending on whether
// the centre of the pixel in within the disk or not. This inaccuracy becomes
// more important when the pixel size become large compared to the disk width.

// This class contains functions that return the Fourier transform of the
// disk at a specified spatial frequency. There are described more fully
// in the description of the <src>visibility</src> functions below.
// </synopsis>

// <example>
// Shown below is code to construct a disk shaped model whose direction is
// always centred on the disk of Jupiter. Note that it is necessary to specify
// the observation time in order for the DiskShape class to be able to do the
// conversion into J2000 coordinates. This example is also available in the
// <src>dTwoSidedShape.cc</src> file. 
// <srcblock>
// { // construct a model for Jupiter.
//   casacore::Quantity clk_time; casacore::MVTime::read(clk_time, "01-10-2000/12:59");
//   casacore::MEpoch obs_epoch(clk_time, casacore::MEpoch::UTC);
//   casacore::MeasFrame obs_frame(obs_epoch);
//   casacore::MDirection jupiter_dir(casacore::MVDirection(0), 
//                          casacore::MDirection::Ref(casacore::MDirection::JUPITER, obs_frame));
//   DiskShape jupiter_shape;
//   jupiter_shape.setRefDirection(jupiter_dir);
//   jupiter_shape.setWidth(casacore::Quantity(4,"arcmin"), casacore::Quantity(3.9,"arcmin"),
//                          casacore::Quantity(3, "deg"));
//   printShape(jupiter_shape);
//   casacore::MDirection sample_dir(casacore::MVDirection(1.218, 0.37), casacore::MDirection::J2000);
//   if (jupiter_shape.sample(sample_dir, casacore::MVAngle(0.1)) > 0.0) {
//     cout << "The position in J2000 coordinates is near: " 
//          << sample_dir.getAngle("deg") << endl;
//   }
// }
// </srcblock>
// The printShape function is the example shown for the TwoSidedShape class.
// </example>
//
// <todo asof="1999/11/12">
//   <li> Use Measures & Quanta in the interface to the visibility functions.
//   <li> Use a better way of integrating over the pixel area in the sample
//        function. 
//   <li> Ensure that the position angle is always between zero and 180
//        degrees, and that the widths are always positive.
// </todo>

// <linkfrom anchor="GaussianShape" classes="ComponentShape TwoSidedShape PointShape GaussianShape">
//  <here>DiskShape</here> - a uniform brightness disk shape.
// </linkfrom>


class DiskShape: public TwoSidedShape
{
public:
    // The default GaussianShape is at the J2000 North Pole with a width of 1
    // arc-min on both axes.
    DiskShape();

    // Construct a disk shape centred in the specified direction, specifying
    // the widths & position angle.
    // <group>
    DiskShape(
        const casacore::MDirection& direction,
        const casacore::Quantum<casacore::Double>& majorAxis,
        const casacore::Quantum<casacore::Double>& minorAxis,
        const casacore::Quantum<casacore::Double>& positionAngle
    );
  
    DiskShape(
        const casacore::MDirection& direction, const casacore::Quantum<casacore::Double>& width,
        const casacore::Double axialRatio,
        const casacore::Quantum<casacore::Double>& positionAngle
    );
    // </group>

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

    // The destructor does nothing special.
    virtual ~DiskShape();

    // The assignment operator uses copy semantics.
    DiskShape& operator=(const DiskShape& other);

    // get the type of the shape. This function always returns
    // ComponentType::DISK.
    virtual ComponentType::Shape type() const;

    // set or return the width and orientation of the disk. All numerical
    // values are in radians. There are also functions in the base class for
    // doing this with other angular units.
    // <group>
    virtual void setWidthInRad(
        const casacore::Double majorAxis,
        const casacore::Double minorAxis,
        const casacore::Double positionAngle
    );

    virtual casacore::Double majorAxisInRad() const;

    virtual casacore::Double minorAxisInRad() const;

    virtual casacore::Double positionAngleInRad() const;
    // </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;

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

    // 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 reference position for the transform is the direction of the
    // component. As this component is symmetric about this point the transform
    // is always a real value.
    virtual casacore::DComplex visibility(
        const casacore::Vector<casacore::Double>& uvw,
        const casacore::Double& frequency
    ) const;

    // Same as the previous function except that many (u,v,w) points can be
    // sampled at once. The uvw casacore::Matrix must have a first dimension of three, and
    // a second dimension that is the same as the length of the scale
    // Vector. Otherwise and exception is thrown (when compiled in debug mode).
    virtual void visibility(
        casacore::Vector<casacore::DComplex>& scale, const casacore::Matrix<casacore::Double>& uvw,
        const casacore::Double& frequency
    ) const;

    //same as above except with many frequencies
    virtual void visibility(
        casacore::Matrix<casacore::DComplex>& scale, const casacore::Matrix<casacore::Double>& uvw,
        const casacore::Vector<casacore::Double>& frequency
    ) 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;

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

    // return a pointer to this object.
    virtual const ComponentShape* getPtr() const; 

    virtual casacore::String sizeToString() const;

private:
    // area of disk
    casacore::Double _area() const;

    casacore::Double _calcSample(
        const casacore::MDirection::MVType& compDirValue, 
        const casacore::MDirection::MVType& dirVal, 
        const casacore::Double majRad, const casacore::Double minRad, 
        const casacore::Double pixValue
    ) const;
  
    casacore::Double _calcVis(
        casacore::Double u, casacore::Double v, const casacore::Double factor
    ) const;

    static void _rotateVis(
        casacore::Double& u, casacore::Double& v, 
        const casacore::Double cpa, const casacore::Double spa
    );

    // The parameters of the disk
    // <group>
    casacore::Double _majorAxis;
    casacore::Double _minorAxis;
    casacore::Double _pa;
    casacore::Double _recipArea;
    // </group>
};

}

#endif