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

/****

!!! The NAL9602-LP requires the following active profile to be set on the bench prior to use in the vehicle !!!

Set +PNAV=4 for at sea operation

Set ^GRS=3,30 for GPS safety reset

Echo Off



ACTIVE PROFILE:

E0 Q0 V1 ^CAL1 ^ID0 ^RMF1 ^GAO0 ^TMD1 ^TBR5.0 ^TTKT255 ^UE0 ^START0

+IPR6 +SBDMTA1 +SBDAREG0 +CIER0000 *R1 *S1 +PP1 +PNAV4 ^BIGR0 ^ERF0

^TBRT0.0 ^TTKTT255 ^TBRE0.0 ^TTKTE255 ^CALT1 ^CALE1

^TBRA3.0 ^TTKTA255 ^TBRTA1.0 ^TTKTTA255 ^TBREA1.0 ^TTKTEA255

^PR0 ^WFI0 ^ITE0 ^MSA000,0,0 ^MSW10 ^SPSR0,10,4,120 ^SSR0

^MSB3,10 ^MSE3 ^MSWT5 ^MSWE5 ^S0,0,0,0,0 ^SR3 ^STEW0

^SPSRT0,10,4,120 ^SPSRE0,10,4,120

^USAIG0 ^BASMR0,0,0 ^GRS3,30

***/

#ifndef NAL9602_H
#define NAL9602_H

#include "component/SyncComponent.h"
#include "data/StrValue.h"
#include "io/SendPacket.h"
#include "io/UartStream.h"
#include "io/LoadControl.h"
#include "logger/Logger.h"
#include "utils/Timestamp.h"
#include "utils/FlexArray.h"
#include "NAL9602IF.h"

#include <stdint.h>

class UniversalDataReader;
class UniversalDataWriter;

#define MAX_GPS_SATS_9602 NAL9602IF::MAX_GPS_SATS_9602
#define MAX_SATS_PER_LINE 4
#define MAX_DOWNLINK_MSG_BYTES 340L
#define MAX_DOWNLINK_DATA_BYTES ( MAX_DOWNLINK_MSG_BYTES - SENDPACKET_HEADER_SIZE )
/**
 *  Provides software interface to the NAL Research NAL9602
 *  Iridium modem and compass
 *
 *  NAL9602.h should only be included by NAL9602.cpp
 *  Other classes should include NAL9602IF.h
 *
 *  \ingroup modules_sensor
 */
class NAL9602: public SyncSensorComponent
{
public:

    NAL9602( const Module* module );

    virtual ~NAL9602();

    virtual void run();

    /// Do what needs to be done to run
    /// Similar to initialize, in old init/run/uninit sequence
    virtual RunState start();

    /// Might follow a STOP...START sequence
    virtual RunState starting();

    /// Pause for a short period (indicated by pauseTime)
    virtual RunState pause();

    /// Should eventually follow a PAUSE request: should set continueTime
    virtual RunState paused();

    /// Resume from PAUSE
    virtual RunState resume();

    /// Might follow a PAUSE...RESUME sequence
    virtual RunState resuming();

    /// Should eventually follow a START request or RESETTING
    virtual RunState runnable();

    /// Might occur in case of Error
    virtual RunState resetting()
    {
        return stop();
    }

    /// Initial state -- can be later followed by START
    virtual RunState stop();

    virtual RunState stopping();

    /// Initial state -- can be later followed by START
    virtual RunState stopped();

    void uninitialize();

    /// Should return [myNamespace]::SIMULATE_HARDWARE, or [myNamespace]::POWER, etc
    virtual ConfigURI getConfigURI( ConfigOption configOption ) const;

    /// Should return [myNamespace]::POWER_[forElement], [myNamespace]::LATENCY_[forElement], etc
    virtual ConfigURI getConfigURI( ConfigOption configOption, ElementURI forElement ) const;

    enum StrobeMode
    {
        STROBE_ON = 0,
        STROBE_OFF,
        STROBE_TEMP_OFF,
        STROBE_TRANSITION
    };


private:

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

    // Slate inputs
    UniversalDataReader* depthReader_;
    UniversalDataReader* latitudeReader_;
    UniversalDataReader* longitudeReader_;

    DataReader* strobeModeReader_;

    // Slate outputs
    DataWriter* numSatellitesWriter_;
    DataWriter* goodFixWriter_;
    DataWriter* sigQualityWriter_;
    DataWriter* sogWriter_;
    DataWriter* cogWriter_;

    UniversalDataWriter* timeFixWriter_;
    UniversalDataWriter* latitudeFixWriter_;
    UniversalDataWriter* longitudeFixWriter_;
    UniversalBlobWriter* locationFixWriter_;
    UniversalDataWriter* communicationsWriter_;

    /// Configuration readers
    ConfigReader* surfaceThresholdCfgReader_;
    ConfigReader* sendDataToShoreCfgReader_;
    ConfigReader* checkMTQueueCfgReader_; // Tells the component whether to explicitly check for messages queued for the NAL9602 even if an Iridium transaction isn't required.
    ConfigReader* gpsFailTimeoutCfgReader_;
    ConfigReader* iridiumFailTimeoutCfgReader_; // How long to wait for an Iridium session to check for waiting MT SBD messages in the queue
    ConfigReader* requestGGACfgReader_;
    ConfigReader* fastGPSFixReader_; // Leaves the power applied to the NAL in order to allow for faster GPS fixes
    ConfigReader* handleZDACfgReader_; // Deals with incoming ZDA messages. For use with mapping LRAUV.
    ConfigReader* maxDownlinkMsgSizeReader_;

    // Used to save state since there are long potential waiting periods for this device to respond
    enum CommsState
    {
        SLEEPING,
        WAKING,
        CMDMODE,
        AWAKE,
        NEED_FIX,
        NEED_GSV,
        GET_SIG_QUALITY,
        SENDING_FILL_BUFFER,
        SENDING_TRANSMIT,
        SENDING_VERIFY,
        RECEIVING_1,
        RECEIVING_2,
        MTQUEUE_QUALITY,
        MTQUEUE_SESSION,
        GO_TO_SLEEP
    };

    /// We define a method for each state of the device
    void waking();
    void cmdMode();
    void awake();
    void needFix();
    void needGSV(); // Gets the GPGSV data from the GPS.
    void getSigQuality();
    void sendingFillBuffer();
    void sendingTransmit();
    void sendingVerify();
    void receiving1();
    void receiving2();
    void goToSleep();
    void getMTQueueSignalStrength(); // Used only for session initiation for checking status of MT queue. Doesn't alter communicationsDone_
    void sessionMTQueueVerify(); // Used only for session initiation verification for checking status of MT queue.


    struct SNRWriters
    {
        // Constructor
        SNRWriters();

        // And the writer
        DataWriter *snrWriter_;
    };

    // Array to store the battery packs and their associated writers
    SNRWriters snrWriters_[MAX_GPS_SATS_9602];

    // To hold our state
    CommsState commsState_;

    /// Stores the response from the modem
    char modemResponse_[512];

    /// Stores the decrypted command
    char command_[512];

    Timespan powerDownTimeout_, poResetTimeout_;
    Timespan requestTimeout_;
    Timespan gpsFailTimeout_;
    Timespan iridiumMTQueueTimeout_;
    Timespan bufferFillTimeout_;

    Timestamp startTime_, powerOnTimeStart_, powerOffTimeStart_, needFixStartTime_, utcTime_, needMTQueueStartTime_;

    bool gotFix_;
    bool gotMTQueue_; // To prevent having to re-check queue status every time needGSV gets called. Just do it once per GPS fix.

    FlexArray<int*> snrData_;

    // indicates whether things are ok to run
    bool ok_;

    ///*------------------- vehicle parameters --------------------------*/
    float surfaceThreshold_;
    bool sendDataToShore_;
    bool checkMTQueue_; // Tells the component whether to explicitly check for messages queued for the NAL9602 even if an Iridium transaction isn't required.
    StrValue keyText_;
    int requestGGA_;
    int fastGPS_;
    int handleZDAMessages_;
    int maxDownlinkMsgSize_, maxDownlinkDataSize_;
    ///////////////////////////////////

    // initialize vehicle config variables
    bool loadParams( void );
    bool readConfig();

    // Used for storing status of messages coming from GSS to NAL9602
    int uplinkStatus_, uplinkSeqNo_;
    unsigned int nUplinkBytes_, nPendingUplinks_;

    // Lets us know when we've got nothing left to send back to shore
    bool communicationsDone_;

    // True if a command has been received from shore since the last time power was applied
    bool cmdReceived_;

    // True if a fix request has been sent
    bool fixRequested_;

    // We're counting down to throw a failure if we haven't gotten a fix in the required time.
    bool gpsTimeoutArmed_;

    // How long we spend trying to check the Iridium MT queue
    bool iridiumTimeoutArmed_;

    // True if a GSV NMEA request has been sent
    bool gsvRequested_;

    // True if an Iridium signal quality request has been sent
    bool qualityRequested_;

//    // Send the current queue of outgoing messages and recieves anything
//    // in the queue from the GSS.
////    void sendAndReceive();

    /// Send (downlink) a message
    //bool sendMessage( const unsigned char *msg, size_t nMsgBytes );

    // Fills the buffer for next time a SBD session is initiated
    bool fillBuffer( const unsigned char *msg, size_t nMsgBytes );

    // Checks the response from filling the buffer and looks for errors.
    // Then initiates an SBD session
    bool initiateSession();

    // Checks the response from session initiation
    // And verifies that the data was indeed transmitted
    bool verifySessionXmit();

    // Clears the mobile originated buffer via the +SBDD0 command
    bool clearMOBuffer();

//    /// Get recieved (uplinked) message(s) through
//    /// modem and add to uplink queue
//    int getIncomingMessages();

    /// Retrieve (uplink) message from modem
    /// and queue it for clients
    bool retrieveAndQueueIncoming( int nUplinkBytes );

    // returns true if data is requested from one of the readers
    bool isDataRequested();

    // Debugging outputs
    bool debug_;

    UartStream uart_;
    LoadControl loadControl_;

    // "Quality" as defined by NMEA standard.
    // In the case of the NAL9602-LP:
    // 0 = Invalid Fix, 1 or 2 = Valid Fix, 3 = Estimated Dead Reckoning
    enum DataQuality
    {
        INVALID,
        VALID_ONE,
        VALID_TWO,
        DEAD_RECKON
    };

    enum Hemisphere
    {
        NORTHERN_HEMI,
        SOUTHERN_HEMI,
        EASTERN_HEMI,
        WESTERN_HEMI,
        INVALID_HEMISPHERE
    };

    //
    // Save Gps time in the following structure:
    //
    struct NmeaUTCTime
    {
        // Constructor
        NmeaUTCTime();

        int hours_;
        int minutes_;
        int seconds_;
        int centiSeconds_;
    };

    //
    // Save GPS date in the following structure:
    //
    struct NmeaUTCDate
    {
        // Constructor
        NmeaUTCDate();

        int day_;
        int month_;
        int year_;
    };

    /// Data structure contains items specified by the
    /// NMEA GPS data standard.
    struct Fix
    {
        // Constructor
        Fix();

        /// Timestamp, in NmeaUTCTime format
        NmeaUTCTime utctime_;

        /// Datestamp
        NmeaUTCDate utcdate_;

        // Latitude (degrees)
        double latitude_;

        /// North/South hemisphere
        Hemisphere nsHemisphere_;

        // Longitude (degrees)
        double longitude_;

        /// East/West hemisphere
        Hemisphere ewHemisphere_;

        /// Status
        bool status_; // RMC only. A==valid, V==invalid

        /// Speed Over Ground
        float sog_;

        /// Course Over Ground
        float cog_;

        /// Data quality (see above)
        DataQuality quality_;

        /// Number of satellites used in fix
        short nSatellites_;

        /// Horizontal dilution of precision
        float hdop_;

        /// Altitude above mean sealevel in meters
        float altitude_;

        /// Geoid height above mean sealevel in meters
        float geoidHeight_;

        /// DGPS fix age in seconds
        short dgpsDataAge_;

        /// DGPS reference station id
        short dgpsStationId_;
    };

    Fix fix_;

    /// Stores modem signal quality (0-5), -1 on error
    void getReceivedSignalStrength();

    /// Holds most recent Iridium signal quality
    short sigQuality_;

    bool belowSurfaceThreshold( void );

    /// Returns position from GPS or an empty string upon error
    bool getFix( void );

    /// sends request to get a fix and returns when data is ready or timeout has occured.
    bool requestFix( void );

    /// sends request to get GSV NMEA string and returns when data is ready or timeout has occured.
    bool requestGSV( void );

    enum SentenceType
    {
        GPGGA, GPGSA, GPRMC, GPZDA, UNKNOWN_NMEA_TYPE
    };

    /// Returns type of NMEA sentence
    static SentenceType GetNmeaSentenceType( const char *sentence );

    /// Parse Gps fix from NMEA "GGA sentence".
    bool parseGGAFix( const char *ggaSentence );

    /// Parse Gps fix from NMEA "RMC sentence".
    bool parseRMCFix( const char *rmcSentence );

    /// Parses the GSV NMEA string(s)
    bool parseGSV( void );

    /// Check to see if a fix has been acquired
    bool goodGGAFixIndicator( const char *ggaSentence );
    bool goodRMCFixIndicator( const char *rmcSentence );

    bool radioEnabled_;

    /// Parse latitude in radians from NMEA standard latitude string
    static double ParseLatitude( const char *nmeaString );

//    /// Print latitude in "dd mm.xx" format. (Input in radians)
//    static char *PrintLatitude( double degrees );

    /// Parse longitude in radians from NMEA standard longitude string
    static double ParseLongitude( const char *nmeaString );

//    /// Print longitude in "ddd mm.xx" format. (Input in radians)
//    static char *PrintLongitude( double degrees );

    /// Returns the hemisphere from the NMEA string
    Hemisphere char2Hemisphere( unsigned char hem );

    /// Controls the strobe
    bool checkStrobe( void );

    /// Name of SBD currently being re-sent to shore
    char* resendSBDFilename_;

    SendPacket sendPacket_;

    StrobeMode strobeMode_;

};

#endif /*NAL9602_H*/
