//# MomentsBase.h: base class for moment generator 
//# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003
//# 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: MomentsBase.h 20299 2008-04-03 05:56:44Z gervandiepen $

#ifndef IMAGES_MOMENTSBASE_H
#define IMAGES_MOMENTSBASE_H

#include <casacore/casa/aips.h>
#include <casacore/coordinates/Coordinates/CoordinateSystem.h>
#include <casacore/casa/Quanta/Quantum.h>
#include <casacore/measures/Measures/MDoppler.h>
#include <casacore/casa/Logging/LogIO.h>
#include <casacore/casa/Arrays/Vector.h>
#include <iosfwd>

namespace casacore{

class IPosition;
class String;
class Unit;
}

namespace casa {

template <class T> class MomentCalcBase;

// <summary>
// This class is a base class for generating moments from an image or a spectral data.
// </summary>

// <use visibility=export>

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

// <prerequisite>
//   <li> <linkto class="ImageMoments">ImageMoments</linkto>   
//   <li> <linkto class="MSMoments">MSMoments</linkto>   
//   <li> <linkto class="casacore::LatticeApply">casacore::LatticeApply</linkto>   
//   <li> <linkto class="MomentCalcBase">MomentCalcBase</linkto>
// </prerequisite>

// <etymology>
//   This class is an abstract class to compute moments from images or spectral data.
// </etymology>

// <synopsis>
//  The primary goal of MSMoments, ImageMoments, and MSMoments are to help 
//  spectral-line astronomers analyze their multi-dimensional images or 
//  spectral data (in the form of casacore::MeasurementSet) by generating moments of 
//  a specified axis.  ImageMoments is a specialized class to generate moments 
//  from images, while MSMoments is designed properly for casacore::MeasurementSet input.
//  MSMoments class is an abstract class that is inherited by the above two 
//  concrete classes.
//  MomentsBase provides interface layer to the MomentCalculators so that 
//  functionalities in MomentCalculators can be shared with ImageMoments and 
//  MSMoments.
//  The word "moment" is used loosely here.  It refers to collapsing an axis
//  to one pixel and putting the value of that pixel (for all of the other 
//  non-collapsed axes) to something computed from the data values along
//  the moment axis.  For example, take an RA-DEC-Velocity cube, collapse
//  the velocity axis by computing the mean intensity at each RA-DEC
//  pixel.  This class and its inheritances offer many different moments and a variety of
//  interactive and automatic ways to compute them. 
//

// <motivation>
//  MSMoments is defined so that moments can be generated from both images 
//  and spectral data (in the form of casacore::MeasurementSet).  
// </motivation>


template <class T> class MomentsBase {
public:

    // Note that if I don't put MomentCalcBase as a forward declaration
    // and use instead  "friend class MomentCalcBase<T>"  The gnu compiler
    // fails with a typedef problem.  But I can't solve it with say
    // <src>typedef MomentCalcBase<T> gpp_type;</src>  because you can only do a
    // typedef with an actual type, not a <tt>T</tt> !
    friend class MomentCalcBase<T>;

    // The <src>enum MethodTypes</src> is provided for use with the
    // <src>setWinFitMethod</src> function.  It gives the allowed moment
    // methods which are available with this function.  The use of these
    // methods is described further with the description of this function
    // as well as in the general documentation earlier.
    enum MethodTypes {
        // Invokes the spectral windowing method
        WINDOW,
        // Invokes Gaussian fitting
        FIT,
        NMETHODS
    };

    // This <src>enum MomentTypes</src> is provided for use with the
    // <src>setMoments</src> function.  It gives the allowed moment
    // types that you can ask for.

    enum MomentTypes {
        // The average intensity
        AVERAGE,
        // The integrated intensity
        INTEGRATED,
        // The intensity weighted mean coordinate (usually velocity)
        WEIGHTED_MEAN_COORDINATE,
        // The intensity weighted coordinate (usually velocity) dispersion
        WEIGHTED_DISPERSION_COORDINATE,
        // The median intensity
        MEDIAN,
        // The median coordinate (usually velocity). Treat the spectrum as
        // a probability distribution, generate the cumulative distribution,
        // and find the coordinate corresponding to the 50% value.
        MEDIAN_COORDINATE,
        // The standard deviation about the mean of the intensity
        STANDARD_DEVIATION,
        // The rms of the intensity
        RMS,
        // The absolute mean deviation of the intensity
        ABS_MEAN_DEVIATION,
        // The maximum value of the intensity
        MAXIMUM,
        // The coordinate (usually velocity) of the maximum value of the intensity
        MAXIMUM_COORDINATE,
        // The minimum value of the intensity
        MINIMUM,
        // The coordinate (usually velocity) of the minimum value of the intensity
        MINIMUM_COORDINATE,
        // Total number
        NMOMENTS,
        // Default value is the integrated intensity
        DEFAULT = INTEGRATED
    };
    
    // Destructor
    virtual ~MomentsBase();

    // Set the desired moments via an <src>casacore::Int</src> array.  Each <src>casacore::Int</src>
    // specifies a different moment; the allowed values and their meanings
    // are given by the <src>enum MomentTypes</src>.   A return value
    // of <src>false</src> indicates you asked for an out of range
    // moment.  If you don't call this function, the default state of the
    // class is to request the integrated intensity.
    casacore::Bool setMoments (const casacore::Vector<casacore::Int>& moments);

    // Set the moment axis (0 relative).  A return value of <src>false</src>
    // indicates that the axis was not contained in the image. If you don't
    // call this function, the default state of the class is to set the
    // moment axis to the spectral axis if it can find one.  Otherwise
    // an error will result.
    virtual casacore::Bool setMomentAxis (casacore::Int) = 0;

    // The method by which you compute the moments is specified by calling
    // (or not calling) the <src>setWinFitMethod</src> and
    // <src>setSmoothMethod</src> functions.  The default state of the class
    // is to compute directly on all (or some according to <src>setInExClude</src>)
    // of the pixels in the spectrum.  Calling these functions modifies the
    // computational state to something more complicated.
    //
    // The <src>setWinMethod</src> function requires an <src>casacore::Int</src> array
    // as its argument.  Each <src>casacore::Int</src> specifies a different method
    // that you can invoke (either separately or in combination).  The
    // allowed values and their meanings are given by the
    // <src>enum MethodTypes</src>.
    //
    // Both the windowing and fitting methods have interactive modes. The
    // windowing method also has a fitting flavour, so if you set both
    // MomentsBase::WINDOW and MomentsBase::FIT, you would be invoking the
    // windowing method but determining the window by fitting Gaussians
    // automatically (as MomentsBase::INTERACTIVE) was not given.
    //
    // If you don't call this function, then neither the windowing nor fitting
    // methods are invoked.  A return value of <src>false</src> indicates
    // that you asked for an illegal method.
    casacore::Bool setWinFitMethod(const casacore::Vector<casacore::Int>& method);

    // This function invokes smoothing of the input image.  Give <src>casacore::Int</src>
    // arrays for the axes (0 relative) to be smoothed and the smoothing kernel
    // types (use the <src>enum KernelTypes</src>) for each axis.  Give a
    // <src>casacore::Double</src> array for the widths (full width for BOXCAR and full
    // width at half maximum for GAUSSIAN) in pixels of the smoothing kernels for
    // each axis.  For HANNING smoothing, you always get the quarter-half-quarter
    // kernel (no matter what you might ask for).  A return value of <src>false</src>
    // indicates that you have given an inconsistent or invalid set of smoothing
    // parameters.  If you don't call this function the default state of the
    // class is to do no smoothing.  The kernel types are specified with
    // the casacore::VectorKernel::KernelTypes enum
    // <group>
    virtual casacore::Bool setSmoothMethod(
            const casacore::Vector<casacore::Int>&, const casacore::Vector<casacore::Int>&,
            const casacore::Vector<casacore::Quantum<casacore::Double> >&
    ) = 0;

    casacore::Bool setSmoothMethod(
        const casacore::Vector<casacore::Int>& smoothAxes,
        const casacore::Vector<casacore::Int>& kernelTypes,
        const casacore::Vector<casacore::Double>& kernelWidths
    );
    // </group>

    // You may specify a pixel intensity range as either one for which
    // all pixels in that range are included in the moment calculation,
    // or one for which all pixels in that range are excluded from the moment
    // calculations.  One or the other of <src>include</src> and <src>exclude</src>
    // must therefore be a zero length vector if you call this function.
    // A return value of <src>false</src> indicates that you have given both
    // an <src>include</src> and an <src>exclude</src> range.  If you don't call
    // this function, the default state of the class is to include all pixels.
    void setInExCludeRange(
        const casacore::Vector<T>& include, const casacore::Vector<T>& exclude
    );

    // This function is used to help assess whether a spectrum along the moment
    // axis is all noise or not.  If it is all noise, there is not a lot of point
    // to trying to computing the moment.
    // <src>peakSNR</src> is the signal-to-noise ratio of the peak value in the
    // spectrum below which the spectrum is considered pure noise.
    // <src>stdDeviation</src> is the standard deviation of the noise for the
    // input image.
    //
    // Default values for one or the other parameter are indicated by giving zero.
    //
    // The default state of the class then is to set <src>peakSNR=3</src>
    // and/or to work out the noise level from a Gaussian fit to a histogram
    // (above 25%) of the entire image (it is very hard to get an accurate
    // estimate of the noise a single spectrum).
    void setSnr(const T& peakSNR, const T& stdDeviation);

    // This is the output file name for the smoothed image.   It can be useful
    // to have access this to this image when trying to get the pixel
    // <src>include</src> or <src>exclude</src> range correct for the smooth-clip
    // method.  The default state of the class is to not output the smoothed image.
    casacore::Bool setSmoothOutName(const casacore::String& smOut);

    // Set Velocity type.  This is used for moments for which the moment axis is
    // a spectral axis for which the coordinate is traditionally presented in
    // km/s   You can select the velocity definition. The default state of the
    // class is to use the radio definition.
    void setVelocityType (casacore::MDoppler::Types type);

    // Reset argument error condition.  If you specify invalid arguments to
    // one of the above functions, an internal flag will be set which will
    // prevent the <src>createMoments</src> function, which is defined in its inheritances,
    // from doing anything
    // (should you have chosen to igmore the Boolean return values of the functions).
    // This function allows you to reset that internal state to good.
    void resetError () {goodParameterStatus_p = true; error_p = "";};

    // Recover last error message
    casacore::String errorMessage() const {return error_p;};

    // Get CoordinateSystem
    virtual const casacore::CoordinateSystem& coordinates() = 0;

    // Helper function to convert a string containing a list of desired methods to
    // the correct <src>casacore::Vector<casacore::Int></src> required for the <src>setWinFitMethod</src> function.
    // This may be usful if your user interface involves strings rather than integers.
    // A new value is added to the output vector (which is resized appropriately) if any of the
    // substrings "window", "fit" or "interactive" (actually "win", "box" and
    // "inter" will do) is present.
    static casacore::Vector<casacore::Int> toMethodTypes (const casacore::String& methods);

    virtual casacore::IPosition getShape() const = 0;

    casacore::Bool shouldConvertToVelocity() const { return convertToVelocity_p; }

protected:

    // Constructor takes an image and a <src>casacore::LogIO</src> object for logging purposes.
    // You specify whether output images are  automatically overwritten if pre-existing,
    // or whether an intercative choice dialog widget appears (overWriteOutput=F)
    // You may also determine whether a progress meter is displayed or not.
    MomentsBase(
        casacore::LogIO &os, casacore::Bool overWriteOutput=false,
        casacore::Bool showProgress=true
    );

    casacore::LogIO os_p;
    casacore::Bool showProgress_p;
    casacore::Int momentAxisDefault_p {-10};
    T peakSNR_p {T(3)};
    T stdDeviation_p {T(0)};
    T yMin_p {T(0)};
    T yMax_p {T(0)};
    casacore::String out_p;
    casacore::String smoothOut_p {};
    casacore::Bool goodParameterStatus_p {true};
    casacore::Bool doWindow_p {false};
    casacore::Bool doFit_p {false};
    casacore::Bool doSmooth_p {false};
    casacore::Bool noInclude_p {true};
    casacore::Bool noExclude_p {true};
    casacore::Bool fixedYLimits_p {false};

    casacore::Int momentAxis_p {momentAxisDefault_p};
    casacore::Int worldMomentAxis_p;
    casacore::Vector<casacore::Int> kernelTypes_p {};
    casacore::Vector<casacore::Quantity> kernelWidths_p {};
    casacore::Vector<casacore::Int> moments_p {1, INTEGRATED};
    casacore::Vector<T> selectRange_p {};
    casacore::Vector<casacore::Int> smoothAxes_p {};
    casacore::Bool overWriteOutput_p;
    casacore::String error_p {};
    casacore::Bool convertToVelocity_p {false};
    casacore::MDoppler::Types velocityType_p {casacore::MDoppler::RADIO};

    // Check that the combination of methods that the user has requested is valid
    // casacore::List a handy dandy table if not.
    void _checkMethod();

    // Take the user's data inclusion and exclusion data ranges and
    // generate the range and Booleans to say what sort it is
    void _setIncludeExclude (
        casacore::Vector<T>& range, casacore::Bool& noInclude,
        casacore::Bool& noExclude, const casacore::Vector<T>& include,
        const casacore::Vector<T>& exclude
    );

    // Set the output image suffixes and units
    casacore::Bool _setOutThings(
        casacore::String& suffix, casacore::Unit& momentUnits,
        const casacore::Unit& imageUnits, const casacore::String& momentAxisUnits,
        const casacore::Int moment, casacore::Bool convertToVelocity
    );

    // Make output Coordinates
    casacore::CoordinateSystem _makeOutputCoordinates(
        casacore::IPosition& outShape, const casacore::CoordinateSystem& cSysIn,
        const casacore::IPosition& inShape, casacore::Int momentAxis,
        casacore::Bool removeAxis
    );
};

}

#ifndef AIPS_NO_TEMPLATE_SRC
#include <imageanalysis/ImageAnalysis/MomentsBase.tcc>
#endif

#endif