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

#ifndef LCMDATAWRITER_H_
#define LCMDATAWRITER_H_

#include <lcm/lcm.h>
#include <lcm/lcm-cpp.hpp>
#include "DataElement.h"
#include "component/Component.h"
#include "data/DataWriter.h"
#include "data/DataValue.h"
#include "utils/Mutex.h"
#include "utils/Timestamp.h"
#include "lrauv-lcmtypes/raw/raw/bytes_t.hpp"
#include "lrauv-lcmtypes/raw/raw/u8_t.hpp"
#include "lrauv-lcmtypes/raw/raw/i32_t.hpp"
#include "lrauv-lcmtypes/raw/raw/double_t.hpp"
#include "LcmPolicy.h"
#include "logger/Logger.h"

class Unit;
class UniversalDataElement;
class DataWriter;
union LcmMessages;

/// @typedef union LcmMessages_u LcmWriterMessage
/// @brief Union of LCM message types supported by LcmDataWriter
typedef union LcmMessages LcmWriterMessage;

/// @union LcmMessages
/// @brief Union of LCM message types supported by LcmDataWriter
union LcmMessages
{
    /// @var LcmMessages::imsg_
    /// @brief integer message type
    raw::i32_t imsg_;
    /// @var LcmMessages::dmsg_
    /// @brief integer message type
    raw::double_t dmsg_;
    /// @var LcmMessages::umsg_
    /// @brief integer message type
    raw::u8_t umsg_;
};


/**
 *  A DataAccessor that writes values to the Slate
 *  via its associated DataElement, and optionally, to LCM
 *
 *  \ingroup data
 */
class LcmDataWriter : public DataWriter
{
public:
    enum  LcmMsgType { U8 = 0, I32, DOUBLE};

    /// @fn LcmDataWriter( Component* owner, DataElement& element, UniversalDataElement* universal,Logger::TimePrecisionType timePrecisionType, Str source, Str channel, LcmPolicy *policy=NULL)
    /// @brief Initializing constructor.
    /// @param[in] owner
    /// @param[in] element
    /// @param[in] universal
    /// @param[in] timePrecisionType
    /// @param[in] source
    /// @param[in] channel
    /// @param[in] policy
    /// @return true on success, false otherwize.
    LcmDataWriter( Component* owner,
                   DataElement& element,
                   UniversalDataElement* universal,
                   Logger::TimePrecisionType timePrecisionType,
                   Str source,
                   Str channel,
                   LcmPolicy *policy = NULL );

    /// @fn virtual ~LcmDataWriter()
    /// @brief virtual destructor.
    virtual ~LcmDataWriter();

    /**
     *  Sets the Component's DataValue, with an optional timestamp
     *  - Meets requirement \ref req_timing_timeStamp
     *  - Publishes to LCM (per policy)
     */
    virtual bool write( const Unit& unit, const unsigned char value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool write( const Unit& unit, const double value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool write( const Unit& unit, const int value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool write( const DataValue& value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );

    /// @fn void setPolicy(LcmPolicy *policy)
    /// @brief Set new LCM provider. Affects all instances.
    /// Setting policy to NULL disables LCM publishing.
    /// @param[in] policy new policy (caller owns, must release resources)
    /// @return none.
    void setPolicy( LcmPolicy *policy );

protected:
    ///@var Mutex Mutex_
    ///@brief Mutex for thread-safing an instance of DataElement.
    static Mutex Mutex_;
    ///@var LcmPolicy *policy_
    ///@brief Policy (Slate, LCM access permissions, conditions)
    LcmPolicy *policy_;
    ///@var Str channel_;
    ///@brief LCM channel name for this DataWriter (DataElement)
    Str channel_;
    ///@var Str source_;
    ///@brief Source name (to distinguish LCM data publishers)
    Str source_;
    ///@var LcmMessages message_;
    ///@brief LCM message union
    LcmWriterMessage message_;
    ///@var Logger logger_
    ///@brief Log instance
    Logger logger_;

    /// @fn bool publish(LcmWriterMessage msg, LcmDataWriter::LcmMsgType type);
    /// @brief publish data to LCM
    /// @param[in] msg an LCM message (union of supported message types)
    /// @param[in] type enumerated message type code
    /// @return true on success, false otherwise
    bool publish( LcmWriterMessage msg, LcmDataWriter::LcmMsgType type );

private:
    // don't allow copy constructor
    LcmDataWriter( const LcmDataWriter& );
    LcmDataWriter& operator= ( const LcmDataWriter& );

};

#endif /*LCMDATAWRITER_H_*/
