/** \file
 *
 *  Contains the PeakDetectHorizontal class declaration.
 *
 *  PeakDetectHorizontal.h should only be included by PeakDetectHorizontal.cpp
 *  Other classes should include PeakDetectHorizontalIF.h
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef PEAKDETECTTHRESHOLD_H_
#define PEAKDETECTTHRESHOLD_H_

#include "component/Behavior.h"
#include "data/Location.h"

#define MAX_NUMPROFILES_SLIDINGWINDOW 500

class Location;
class UniversalDataReader;

/**
 *  Contains the PeakDetectHorizontal Behavior
 *
 *  PeakDetectHorizontal.h should only be included by PeakDetectHorizontal.cpp
 *  Other classes should include PeakDetectHorizontalIF.h
 *
 *  \ingroup modules_trigger
**/

class PeakDetectHorizontal : public Behavior
{
public:

    PeakDetectHorizontal( const Str& prefix, const Module* module );

    virtual ~PeakDetectHorizontal();

    /// Initialize function
    void initialize( void );

    /// Read in the parameters for satisfied or runIfUnsatisfied: return true if OK.
    bool readParams();

    /// Perform the satisfied: return true if envelope "satisfied"
    bool calcSatisfied();

    /// Just do the run: ignore the results of the satisfied
    void run();

    /// Just do the satisfied: return true if envelope "satisfied"
    bool isSatisfied();

    /// Do the run, and return true if envelope "satisfied"
    bool runIfUnsatisfied();

    /// Uninit function
    void uninitialize( void );

    /// Mission Component factory interface
    static Behavior* CreateBehavior( const Str& prefix, const Module* module );

protected:

    /// if no "filterWidth" setting is provided, what to use
    static const int DEFAULT_FILTER_WIDTH;

    /// if no "numProfilesSlidingwindow" setting is provided, what to use
    static const int DEFAULT_NUMPROFILES_SLIDINGWINDOW;

    // Slate input setting variables

    /// Sets variable to detect the peak of (i.e., chl concentration)
    SettingReader *detectFromSettingReader_;

    /// Sets depth variable associated with the peak
    SettingReader *depthFromSettingReader_;

    /// Sets latitude variable associated with the peak
    SettingReader *latitudeFromSettingReader_;

    /// Sets latitude variable associated with the peak
    SettingReader *longitudeFromSettingReader_;

    /// Sets advectValues (if true, advect values in space)
    SettingReader *advectValuesSettingReader_;

    /// Sets patchTracking (if true, tracking a patch)
    SettingReader *patchTrackingSettingReader_;

    /// Sets detectTrough (if true, detect a low value)
    SettingReader *detectTroughSettingReader_;

    /// Sets beginThreshold
    SettingReader *beginThresholdSettingReader_;

    /// Sets offPeakFraction
    SettingReader *offPeakFractionSettingReader_;

    /// Sets filterWidth
    SettingReader *filterWidthSettingReader_;

    /// Sets numProfilesSlidingwindow
    SettingReader *numProfilesSlidingwindowSettingReader_;

    /// Sets centerPeak
    SettingReader *centerPeakSettingReader_;

    /// Sets detectTimeout
    SettingReader *detectTimeoutSettingReader_;

    // Actual values from settings

    const Unit* detectFromBaseUnit_;

    int advectValuesSetting_;

    int patchTrackingSetting_;

    int detectTroughSetting_;

    float beginThresholdSetting_;

    float offPeakFractionSetting_;

    int filterWidthSetting_;

    int numProfilesSlidingwindowSetting_;

    int centerPeakSetting_;

    Timespan detectTimeout_;

    float detectTimeoutSetting_;

    // Slate input measurements

    /// Reads distance travelled with respect to sea water
    DataReader* distanceWrtSeaWaterReader_;

    /// Reads depth
    DataReader* depthReader_;

    /// Reads latitude
    DataReader* latitudeReader_;

    /// Reads longitude
    DataReader* longitudeReader_;

    /// Reads eastward sea water velocity
    DataReader* eastwardSeaWaterVelocityReader_;

    /// Reads northward sea water velocity
    DataReader* northwardSeaWaterVelocityReader_;

    // Argument output variables
    DataWriter* peakDetectWriter_;
    DataWriter* peakDepthWriter_;
    DataWriter* peakLatitudeWriter_;
    DataWriter* peakLongitudeWriter_;
    DataWriter* peakDistanceWriter_;

    /// Instantaneous values.
    float detect_;
    float depth_;
    float distance_;
    float latitude_;
    float longitude_;
    float eastwardVelocity_;
    float northwardVelocity_;

    class PointValue
    {
    public:
        PointValue()
            : time_( nanf( "" ) ),
              latitude_( nanf( "" ) ),
              longitude_( nanf( "" ) ),
              distance_( nanf( "" ) ),
              depth_( nanf( "" ) ),
              value_( nanf( "" ) )
        {}
        bool isSet()
        {
            return !isnan( value_ );
        }
        void set( double all )
        {
            set( all, all, all, all, all, all );
        }
        void set( double time, double latitude, double longitude, float distance, float depth, float value )
        {
            time_ = time, latitude_ = latitude, longitude_ = longitude, distance_ = distance, depth_ = depth, value_ = value;
        }
        double getTime()
        {
            return time_;
        }
        double getLatitude()
        {
            return latitude_;
        }
        double getLongitude()
        {
            return longitude_;
        }
        float getDepth()
        {
            return depth_;
        }
        float getDistance()
        {
            return distance_;
        }
        float getValue()
        {
            return value_;
        }
        void advect( const float bearing, const float distance )
        {
            Location::AtBearing( bearing, distance, latitude_, longitude_ );
        }
        bool differsFrom( const float depth, const float value )
        {
            return depth_ != depth || value_ != value;
        }
        PointValue& operator += ( const PointValue& pv )
        {
            time_ += pv.time_, latitude_ += pv.latitude_, longitude_ += pv.longitude_, distance_ += pv.distance_, depth_ += pv.depth_, value_ += pv.value_;
            return *this;
        }
        PointValue& operator /= ( const int div )
        {
            time_ /= div, latitude_ /= div, longitude_ /= div, distance_ /= div, depth_ /= div, value_ /= div;
            return *this;
        }
    protected:
        double time_;
        double latitude_;
        double longitude_;
        float distance_;
        float depth_;
        float value_;
    };

    FlexArray<PointValue*> filterValues_;
    int filterPosition_;
    int historySize_;

    /// Value at start, peak and end of blob.
    PointValue lastValue_;
    PointValue startDetect_;
    PointValue peakDetect_;
    PointValue endDetect_;
    PointValue filtDetect_[MAX_NUMPROFILES_SLIDINGWINDOW];

    Timestamp startTime_;
    bool inPeak_;

    void reportPeak();

private:

    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    PeakDetectHorizontal( const PeakDetectHorizontal& old ); // disallow copy constructor

    void initVars();
};

#endif /*PEAKDETECTTHRESHOLD_H_*/
