/** \file
 *
 *  Contains the NavChartDb class declaration.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef NAVCHARTDB_H_
#define NAVCHARTDB_H_

#include "component/AsyncComponent.h"
#include "data/StrValue.h"
#include "io/EncReader.h"
#include "logger/Logger.h"
#include "utils/FastMap.h"
#include "utils/Mutex.h"

class EncReader;
class DataWriter;
class Mtx;

/**
 *  Reads a standard electronic navigation chart (ENC) in S-57 format,
 *  stores it in a database, and uses the values in the database to estimate
 *  nearest contours, coastline, and soundings of a lat/lon pair.
 *
 *  \ingroup utils
 */
class NavChartDb : public AsyncComponent
{
public:

    NavChartDb( );

    virtual ~NavChartDb();

    virtual void initialize();

    virtual void run();
    void updateCurrent( const float latitude, const float longitude,
                        bool gotFix );
    float getSeaFloorDepth( const float latitude, const float longitude );
    float getDistanceFromShore( const float latitude, const float longitude );
    virtual void uninitialize();

    /// converts a latitude and longitude into a index that covers a small area
    /// of the planet (smaller than GEO_GRID_SIZE * GEO_GRID_SIZE )
    static int CalcGeoIndex( float latitude, float longitude );

    /// Combines latitude index and longitude index into a single index
    static int MakeGeoIndex( int latIndex, int lonIndex );

    /// converts a latitude into a index that covers a small band
    /// around the planet
    static int CalcLatIndex( float latitude );

    /// Returns number of small longitudinal areas in the latIndex band
    /// around the planet (each smaller than GEO_GRID_SIZE * GEO_GRID_SIZE)
    static int CalcNumLonIndices( int latIndex );

    /// Calculates longitudinal index of small area in the latIndex band
    /// around the planet
    static int CalcLonIndex( float longitude, int nLons );

    bool ready()
    {
        return ready_;
    }

    static NavChartDb* GetInstance()
    {
        return Instance_;
    }

    void setOkToUpdateFromDisk()
    {
        okToUpdateFromDisk_ = true;
    }

    void setChartsCfg( const Str chartsCfg );

protected:
    class ClosePoint
    {
    public:
        ClosePoint() :
            depth_( nanf( "" ) ), lat_( nanf( "" ) ), lon_( nanf( "" ) ), cosLat_(
                nanf( "" ) ), angle_( nanf( "" ) ), angleSq_( nanf( "" ) )
        {
        }

        float getdepth()
        {
            return depth_;
        }

        float getlat()
        {
            return lat_;
        }

        float getLon()
        {
            return lon_;
        }

        float getCosLat()
        {
            return cosLat_;
        }

        float getAngle()
        {
            return angle_;
        }

        float getAngleSq()
        {
            return angleSq_;
        }

        void setDepth( const float& depth )
        {
            depth_ = depth;
        }

        void setLat( const float& lat )
        {
            lat_ = lat;
        }

        void setLon( const float& lon )
        {
            lon_ = lon;
        }

        void setCosLat( const float& cosLat )
        {
            cosLat_ = cosLat;
        }

        void setAngle( const float& angle )
        {
            angle_ = angle;
        }

        void setAngleSq( const float& angleSq )
        {
            angleSq_ = angleSq;
        }

        friend bool operator >( const ClosePoint& ptL, const ClosePoint& ptR )
        {
            return ptL.angleSq_ > ptR.angleSq_;
        }

        float depth_;
        float lat_;
        float lon_;
        float cosLat_;
        float angle_;
        float angleSq_;
    };
    struct EncInfo
    {
        EncInfo( int encId, int resLevel, bool loaded, Str& filename ) :
            encId_( encId ),
            resLevel_( resLevel ),
            loaded_( loaded ),
            filename_( filename )
        {
        }

        virtual ~EncInfo() {}

        int encId_;
        int resLevel_;
        bool loaded_;
        Str filename_;
    };

private:
    NavChartDb( const NavChartDb& old );
    void wipeOut();
    void updateClosest();
    bool initDataDir();
    bool saveDepths();
    bool loadDepths();
    bool createIndices();
    bool createSoundings();
    bool createContour( const Str* depthStr );
    bool appendToContour( const Str* depthStr, const int encId,
                          const int resLevel, const float latitude, const float longitude );
    bool appendToSoundings( const float depth, const int encId,
                            const int resLevel, const float latitude, const float longitude );
    bool appendToCoverage( const int encId, const int resLevel,
                           const float x0, const float y0, const float x1, const float y1 );
    bool loadCoverage( const int resLevel, EncArea** coverage );
    bool saveEncs();
    bool loadEncs();
    bool addEncData( EncInfo* encInfo );
    Str* mapDepth( const float depth );
    EncArea* getCoverage( int resLevel );
    float calcDistFromLineSq( const float x, const float y, const float x1,
                              const float y1, const float x2, const float y2 );
    void doIndexing( const Str* depthStr );
    void updateGeoIndices();

    bool initialized_;
    bool ready_;
    bool okToUpdateFromDisk_;
    int changeOkToUpdateFromDiskTo_;

    FastMap< const Str, EncInfo* > encMap_;

    FastMap< const float, Str* > depthMap_;

    FastMap< const Str*, Str* > depthMtxNameMap_;

    FastMap< const Str*, Str* > depthIdxNameMap_;

    FastMap< const Str*, FastMap< const int, Mtx*>* > depthMtxMap_;

    FastMap< const Str*, Mtx* > depthIdxMap_;

    FastMap< const float, ClosePoint* > closeMap_;

    FlexArrayBase currentGeoIndices_;

    unsigned int currentGeoIndex_;

    ClosePoint workingClosePoints_[4];

    ClosePoint* closeSoundings_;

    int encId_;

    FlexArray<EncInfo*> encInfos_;

    FlexArray< EncArea* > coverages_;

    EncReader* encReader_;
    ClosePoint currentPoint_;
    int currentCount_;
    int insertCount_;
    float currentDepth_;
    Str* currentDepthStr_;
    const Str soundingsStr_;
    int indexIndex_;
    int depthIndex_;
    int nextDepthIndex_;
    int changeNextDepthIndexTo_;
    int quadrant_;
    float latitude_;
    float changeLatitudeTo_;
    float longitude_;
    float changeLongitudeTo_;
    bool shoreDone_;
    bool mapDone_;
    StrValue chartsCfg_;
    float cycleTimeoutCfgTmp_;
    Timespan cycleTimeoutCfg_;

    static NavChartDb* Instance_;
    static const Str NAME;

    // No need for a NavChartDbIF.h since no other components seem to read these variables.
    // However, we still want to define const DataURI elements so that we can use the
    // non-deprecated Component::newDataWriter method instead of the deprecated Slate::NewOutputWriter method.
    static const DataURI CLOSEST_DISTANCE;
    static const DataURI NEXT_DISTANCE;
    static const DataURI CLOSEST_DEPTH;
    static const DataURI NEXT_DEPTH;
    static const RestartConfigURI CHARTS_CFG;
    static const RestartConfigURI CYCLE_TIMEOUT_CFG;

    static const float NUM_LATS;
    static const float LAT_INCREMENT;

    DataWriter* closestDistanceWriter_;
    DataWriter* nextDistanceWriter_;
    DataWriter* closestDepthWriter_;
    DataWriter* nextDepthWriter_;

    ConfigReader* chartsCfgReader_;
    ConfigReader* cycleTimeoutCfgReader_;

    Mutex currentMutex_;

    static const Timespan PERIOD;

};

#endif /*NAVCHARTDB_H_*/
