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

#ifndef ACOUSTICMODEM_BENTHOS_ATM900_H
#define ACOUSTICMODEM_BENTHOS_ATM900_H

#include "component/SyncComponent.h"
#include "data/StrValue.h"
#include "data/UniversalDataReader.h"
#include "data/UniversalDataWriter.h"
#include "io/FileInStream.h"
#include "io/FileOutStream.h"
#include "io/LoadControl.h"
#include "io/SendPacket.h"
#include "io/UartStream.h"
#include "logger/Logger.h"
#include "supervisor/SendData.h"

#include <stdint.h>

class UniversalDataReader;
class UniversalDataWriter;

/**
 *  Provides software interface to the Benthos ATM-900 Acoustic Modem.
 *
 *  AcousticModem_Benthos_ATM900.h should only be included by AcousticModem_Benthos_ATM900.cpp
 *  Other classes should include AcousticModem_Benthos_ATM900IF.h
 *
 *  \ingroup modules_sensor
 */
class AcousticModem_Benthos_ATM900 : public SyncSensorComponent
{
public:

    AcousticModem_Benthos_ATM900( const Module* module );

    virtual ~AcousticModem_Benthos_ATM900();

    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();

    virtual void uninitialize();

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

private:

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

    void readConfig();

    ConfigReader* verbosityCfgReader_;
    ConfigReader* txPowerCfgReader_;
    ConfigReader* localAddressCfgReader_;
    ConfigReader* sbdAddressCfgReader_;
    ConfigReader* transponderAddressCfgReader_;
    ConfigReader* surfaceThresholdCfgReader_;
    ConfigReader* sendExpressCfgReader_;
    ConfigReader* sendDataToShoreCfgReader_;

    // Slate inputs
    UniversalDataReader* depthReader_;
    DataReader *queryAddressRequestedReader_;
    DataReader *numberOfPingsRequestedReader_;

    // Slate outputs
    DataWriter* acousticWakeupWriter_;
    DataWriter* rangeRequestReceivedWriter_;
    DataWriter* remoteAddressWriter_;
    DataWriter* localAddressWriter_;
    DataWriter* rangeWriter_;
//    DataWriter* onewayCmdWriter_;

    // Universal outputs
    UniversalDataWriter* rxTimeWriter_;
    UniversalDataWriter* txTimeWriter_;
    UniversalDataWriter* platformCommunicationsWriter_;

    // Incoming data from Send command and SendData behavior
    RingBuffer<SendData*>* sendDataBuffer_;

    // Used to save commnications state
    enum CommsState
    {
        SENDING_FILL_BUFFER,
        SENDING_TRANSMIT,
        SENDING_TRANSMIT_VERIFY,
        SENDING_ACK_WAITING,
        SENDING_VERIFIED,
    };

    // To hold our state
    CommsState commsState_;

    enum OutgoingCommsType
    {
        OUTGOING_SBD,
        OUTGOING_DATA,
        OUTGOING_ACK
    };

    // Used to wrap outgoing comms
    class OutgoingComms
    {
    private:
        Str data_;
        int address_;
        OutgoingCommsType commsType_;
    public:
        OutgoingComms( const Str& data, int address, OutgoingCommsType commsType )
            : data_( data ),
              address_( address ),
              commsType_( commsType )
        {}
        OutgoingComms( const char* data, size_t length, int address, OutgoingCommsType commsType )
            : data_( data, length ),
              address_( address ),
              commsType_( commsType )
        {}
        const Str& getData() const
        {
            return data_;
        }
        int getAddress() const
        {
            return address_;
        }
        OutgoingCommsType getCommsType() const
        {
            return commsType_;
        }
    };
    FlexArray<OutgoingComms*> outgoingCommsBuffer_;
    OutgoingComms* outgoingCommsNow_;

    bool debug_;
    int verbosity_;
    char deviceResponse_[396]; // Stores the current response from the modem

    // Instance of load controller
    LoadControl loadControl_;
    LoadControl loadControl2_;
    bool hasLoadControl2_;
    Timestamp startTime_;
    Timestamp commandModeTimeStart_;
    Timestamp onlineModeTimeStart_;
    Timestamp powerOffTimeStart_;
    Timespan poTimeout_, commandModeTimeout_, onlineModeTimeout_, powerDownTimeout_;

    UartStream uart_; // Holds the communications device

    // Cfg Settings
    StrValue keyText_;
    int verboseCfgSetting_, txPowerCfgSetting_, localAddressCfgSetting_, sbdAddressCfgSetting_, transponderAddressCfgSetting_;
    float surfaceThresholdCfgSetting_;
    int sendExpressCfgSetting_;
    bool sendDataToShoreCfgSetting_;

    // State
    bool commandModeSent_, commandModeAcknowledged_;
    bool onlineModeSent_, onlineModeAcknowledged_;
    bool verboseSettingSent_, verboseSettingAcknowledged_;
    bool txPowerSettingSent_, txPowerSettingAcknowledged_;
    bool localAddressSettingSent_, localAddressSettingAcknowledged_;
    bool currentRemoteAddressSent_, currentRemoteAddressAcknowledged_;

    Timespan acousticResponseTimeout_;
    size_t dataBytesReceiving_, dataBytesReceived_;
    int remoteAddressRequested_, numPingsRequested_; // The query from another component, consisting of the address of the modem from which the AcousticModem_Benthos_ATM900 is requesting data and the number of return pings it is requesting
    int numPingsReceived_;
    bool gotAcousticWakeup_, gotResponseNotReceived_, gotDebugRxMessage_, gotDebugTxMessage_, gotRangeRequestMessage_, gotRangeMessage_;
    bool awaitingAckTransmitAck_;
    int commRate_;

    double range_;
    Timestamp dataTimestamp_;
    Timestamp transmitPingTime_, receivePingTime_;
    Timestamp modemTransmitPingTime_, modemReceivePingTime_;
    double modemTransmitPingEpochSeconds_, modemReceivePingEpochSeconds_;
    int remoteAddress_, localAddress_;
    int currentRemoteAddress_, incomingRemoteAddress_, incomingLocalAddress_;

#define ATM900_MAX_RCV 2048UL
#define ATM900_MAX_SEND 192UL
#define MAX_DOWNLINK_DATA_SIZE ( ATM900_MAX_SEND - SENDPACKET_HEADER_SIZE )
    char commsData_[ ATM900_MAX_RCV ];
    SendPacket sendPacket_;

    // These methods return true if OK, false if an error encountered.
    // Their state of needing to be run/completion are handled by *Acknowledged_ members
    bool enterCommandMode();
    bool enterOnlineMode();
    bool sendVerboseCfgSetting();
    bool sendTxPowerFromCfg();
    bool sendLocalAddressFromCfg();
    bool sendCurrentRemoteAddress();

    // Sends data
    void sendingFillBuffer();
    void sendingTransmit();
    void sendingTransmitVerify();
    void sendingAckWaiting();
    void sendingVerified();

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

    // Returns true if below surface (as defined in Vertical Control)
    bool belowSurfaceThreshold();

    void logVoltageAndCurrent();

    // returns true if power should stay on
    bool keepPowerOn();

    // returns true if platform communications (sbd send) is requested
    bool isCommsRequested();

    // returns true if Send data is available
    bool isSendDataAvailable();

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

    bool gotNewQuery( void );

    Timestamp requestRange( const int remoteAddress, const int numPings );

    int clearUserPrompt( void );

    void readAndParseResponses( void );

    void parseResponses( void );

    void cleanResponse( void );

    bool parseDebugRxMessage( void );

    bool parseDebugTxMessage( void );

    bool parseRangeMessage( void );

    void publishData( void );

};

#endif /*ACOUSTICMODEM_BENTHOS_ATM900_H*/
