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

#ifndef DUSBL_HYDROID_H
#define DUSBL_HYDROID_H

#include "component/SyncComponent.h"
#include "io/LoadControl.h"
#include "io/UartStream.h"
#include "logger/Logger.h"

#include "data/Matrix3x3.h"

class UniversalDataReader;
class UniversalDataWriter;

/**
 *  Provides software interface to the Benthos DUSBL_Hydroid.
 *
 *  DUSBL_Hydroid.h should only be included by DUSBL_Hydroid.cpp
 *  Other classes should include DUSBL_HydroidIF.h
 *
 *  \ingroup modules_sensor
 */
class DUSBL_Hydroid : public SyncSensorComponent
{
public:

    DUSBL_Hydroid( const Module* module );

    virtual ~DUSBL_Hydroid();

    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:

    friend class DUSBL_Hydroid_Test;

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

    void readConfig();

    ConfigReader* verbosityCfgReader_;
    ConfigReader* transmitLockoutCfgReader_;
    ConfigReader* recieveTimeoutCfgReader_;
    ConfigReader* detectionThresholdCfgReader_;
    ConfigReader* xCenterCfgReader_;
    ConfigReader* yCenterCfgReader_;
    ConfigReader* defaultSoundSpeedCfgReader_;
    ConfigReader* defaultTATCfgReader_;

    // Slate inputs
    DataReader* transponderCodeRequestedReader_;
    DataReader* pingRequestedReader_;

    // Universal inputs and outputs
    UniversalDataReader* soundSpeedReader_;
    UniversalDataWriter* contactAddressWriter_;
    UniversalDataWriter* rangeToContactWriter_;
    UniversalDataWriter* rxTimeWriter_;
    UniversalBlobWriter* directionToContactWriter_;

    // Interface outputs
    DataWriter* dusblPingCodeWriter_;

    // Slate outputs
    DataWriter* xAngleWriter_;
    DataWriter* yAngleWriter_;
    DataWriter* travelTimeWriter_;
    DataWriter* latencyWriter_;
    DataWriter* gainWriter_;
    DataWriter* inBandSignalToNoiseWriter_;
    DataWriter* outBandSignalToNoiseWriter_;
    BlobWriter* directionWriter_;

    bool debug_;
    int verbosityCfgSetting_;
    int transmitLockoutCfgSetting_;
    int recieveTimeoutCfgSetting_;
    int detectionThresholdCfgSetting_;
    float xCenterCfgSetting_;
    float yCenterCfgSetting_;
    float turnAroundTimeCfgSetting_;
    int defaultSoundSpeedCfgSetting_;

    float soundSpeed_;

    char deviceResponse_[396]; // Stores the current response from the modem

    // Instance of load controller
    LoadControl loadControl_;
    Timestamp startTime_;
    Timestamp powerOffTimeStart_;
    Timespan poTimeout_, powerDownTimeout_;

    UartStream uart_; // Holds the communications device

    bool commandSent_, commandAcknowledged_;
    Timestamp transmitPingTime_, receivePingTime_;
    Timespan acousticResponseTimeout_;
    Timestamp rearmLockTime_;
    Timespan rearmLockTimeout_; // wait time required for re-arming the DUSBL between pings
    bool waitingForRangeResponse_; // This is set true when we're expecting a response back from a range request
    int transponderCodeRequested_, numPingsRequested_; // The query from another component, consisting of the address of the modem from which the DAT is requesting data and the number of return pings it is requesting
    bool gotRangeMessage_, gotDirectionMessage_, gotResponseNotReceived_;

    float xAngle_, yAngle_, travelTime_,
          latency_, gain_, inBandSignalToNoise_, outBandSignalToNoise_;

    float range_;
    Point3D direction_;

    void logVoltageAndCurrent();

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

    bool gotNewQuery( void );

    Timestamp requestRange( const int remoteAddress );

    static char* GenerateRangeRequest(
        const int transponderCode,
        const int transmitLockoutCfgSetting,
        const int recieveTimeoutCfgSetting,
        const int detectionThresholdCfgSetting,
        const int xCenterCfgSetting,
        const int yCenterCfgSetting,
        const int soundSpeed );

    void readAndParseResponses( void );

    void parseResponses( void );

    bool parseRangeAndDirectionMessage( void );

    // Used by parseRangeAndDirectionMessage and in unit tests
    static bool ParseRangeAndDirectionMessage( const char* deviceResponse, float& xAngle, float& yAngle, float& travelTime,
            float& latency, float& gain, float& inBandSignalToNoise, float& outBandSignalToNoise );

    void publishData( void );

    void anglesToDirection( void );

    void travelTimeToRange( void );

    bool getSimulatedMeasurements( void ); // TODO fix simulator where appropriate so that this method just reads sim outputs and rewrites them to the slate in the appropriate places

};

#endif /*DUSBL_HYDROID_H*/
