/** \file
 *
 *  Contains the InternalEnvSim class declaration.
 *
 *  Warning - this only reads classic NetCDF version 3 data files
 *    with fixed (non-record) dimensions
 *
 *  To make sure a file is compatable use the following ncks
 *    (NetCDF Kitchen Sink) command:
 *
 *    ncks --fix_rec_dmn time -3 -o output.nc3 input.nc
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef InternalEnvSim_H_
#define InternalEnvSim_H_

#include "component/SyncComponent.h"
#include "Simulator.h"
#include "utils/NetCdf3Reader.h"

#include "InternalEnvSimIF.h"

/**
 *  The InternalEnvSim adds lookup of environmental parameters to the base
 *  Simulator class
 *
 *  \ingroup modules_InternalEnvSim
 */
class InternalEnvSim : public SyncSimulatorComponent
{
public:

    /// Constructor.
    InternalEnvSim( const Module* module );

    /// Destructor.
    virtual ~InternalEnvSim()
    {
        uninitialize();
    };
    /// Initialize the simulator
    virtual void initialize( void );

    /// To be replaced by Str::toDouble() when it becomes available from LBL branch
    static double StrToDouble( const char* str, size_t len = Str::NO_POS );

    /// Run the simulation
    virtual void run( void );

    /// Uninitialize the simulation
    virtual void uninitialize( void );

    static void AzimuthalEquidistantToCoordinates( double &Xm, double &Ym, double depLoc, double latDeg, double lonDeg,
            double latProjectionOrigin, double lonProjectionOrigin );

    static void TransverseMercatorToCoordinates( double &Xm, double &Ym, double depLoc, double latDeg, double lonDeg,
            double latProjectionOrigin, double lonCentralMeridian, double falseEasting, double falseNorthing, double scaleCentralMeridian );

protected:

    struct VarData
    {

        // NetCdf3 variable that contains temperature
        NetCdf3Reader::NetCdfVar* var_;

        // temperature array data
        float array_[2][2][2][2];

        // preserve indices to cut down on NetCDF reads
        int indices_[4];

        VarData();
    };

    virtual void configureSensors();

    void azimuthalEquidistantToCoordinates( double &Xm, double &Ym, double depLoc, double latDeg, double lonDeg );

    void transverseMercatorToCoordinates( double &Xm, double &Ym, double depLoc, double latDeg, double lonDeg );

    virtual void simulateSensors( double time, double depthM, double latDeg, double lonDeg, float* values );

    ConfigReader* nc3FileCfgReader_;
    ConfigReader* varCfgReaders_[InternalEnvSimIF::MAX_VARS];
    ConfigReader* attCfgReaders_[InternalEnvSimIF::MAX_ATTS];
    ConfigReader* timeAdjustCfgReader_;

    // Depth specific parameters:
    UniversalDataReader *depthReader_;

    //----------------------------------------------------------------
    // Gps specific parameters:
    UniversalDataReader *latitudeReader_;
    UniversalDataReader *longitudeReader_;

    // Flag
    bool debug_;

    // Status
    bool ok_;

    // Gridded data file reader
    NetCdf3Reader* netCdf3Reader_;

    /// filename of file that contains ocean model run
    Str nc3FileCfgSetting_;

    /// names of variables in ocean model data
    FlexArray<Str*> varNameCfgSettings_;

    /// units of variables in ocean model data
    FlexArray<const Unit*> varUnitCfgSettings_;

    /// global attribute overrides in ocean model data
    NetCdf3::NetCdfAtts cfgAtts_;

    /// If non-NaN, set first sim time to start of vehicle time plus this value
    double timeAdjustCfgSetting_;

    // time array
    float* modelTime_;

    // size of time Array
    unsigned int modelTimeSize_;

    // depth array
    float* modelDepth_;

    // size of depth Array
    unsigned int modelDepthSize_;

    // latitude array
    float* modelLatitude_;

    // size of latitude Array
    unsigned int modelLatitudeSize_;

    // longitude array
    float* modelLongitude_;

    /// size of longitude Array
    unsigned int modelLongitudeSize_;

    /// NetCdf3 variable that contains latitude
    NetCdf3Reader::NetCdfVar* varLatitude_;

    /// NetCdf3 variable that contains longitude
    NetCdf3Reader::NetCdfVar* varLongitude_;

    // projection Y array
    float* modelY_;

    // size of projection Y Array
    unsigned int modelYSize_;

    // projection X array
    float* modelX_;

    // size of projection X Array
    unsigned int modelXSize_;

    // Variables
    VarData* vars_;

    // Allow Cfg-set attributes to override attributes in the netcdf file
    NetCdf3::NetCdfAtt*  findAtt( NetCdf3Reader* nc3Reader, const Str& name );
    bool readAtt( NetCdf3Reader* nc3Reader, void* assignTo, const NetCdf3::NetCdfType assignType, const NetCdf3::NetCdfAtt* att );

    //------------------ Environmental Model interpolation ----------------
    bool interpolate( const float& time, const float& depth, const float& lat, const float& lon,
                      int& timeIndex, int& depthIndex, int& latIndex, int& lonIndex,
                      VarData& var, float& value );

    bool interpolate( const float& time, const float& depth, const float& lat, const float& lon,
                      int& timeIndex, int& depthIndex, int& latIndex, int& lonIndex,
                      int varIndices[4], float varArray[2][2][2][2], NetCdf3Reader::NetCdfVar* var, float& value );

    /// Interpolates a value on two lines, from (x0, y0) to (x1, y1) to (x2, y2)
    /// Does not extrapolate -- rather, it pins values
    float twoLineEstimate( const float x, const float y0, const float y1, const float y2,
                           const float x0, const float x1, const float x2 );

    enum GridMapping
    {
        GRID_MAPPING_NONE,
        GRID_MAPPING_AZIMUTHAL_EQUIDISTANT,
        GRID_MAPPING_TRANSVERSE_MERCATOR,
    };

    // Coordinate mapping transformation name
    GridMapping gridMapping_;

    // Coordinate mapping parameters
    double scaleCentralMeridian_;
    double lonCentralMeridian_;
    double lonProjectionOrigin_;
    double latProjectionOrigin_;
    double falseEasting_;
    double falseNorthing_;

    // Array to speed decompression of compressed data
    int* decompress_;


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

};

#endif /*InternalEnvSim_H_*/
