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

#ifndef DATAELEMENT_H_
#define DATAELEMENT_H_

#include <exception>

#include "data/DataValue.h"
#include "data/UniversalURI.h"
#include "data/DataReader.h"
#include "data/DataWriter.h"
#include "utils/FlexArray.h"
#include "utils/Mutex.h"
#include "utils/Str.h"

class Component;
class CodedStr;
class Slate;
class Unit;
class UniversalDataElement;

/**
 *  Abstact base class for one "element" of the Slate.
 *
 *  A DataElement is a base class for atomic units of data that can be
 *  written to and read from the slate.
 *
 *  \ingroup data
 */
class DataElement
{

    friend class CommandExec; // for access to getDataValue
    friend class DataAccess;
    friend class DataEntry; // for access to getDataValue
    friend class LcmMsgWriter; // for access to getDataValue
    friend class MissionStateLogger; // for access to getDataValue
    friend class Reporter; // for access to getDataValue
    friend class Slate; // for ReadOnce operations
    friend class UniversalDataElement;

public:

    class UninitializedException
    {
    public:
        UninitializedException( /*const Str& message*/ )
        //: message_( message )
        {
            //message_ = message;
        }

        virtual ~UninitializedException()
#ifndef __GAZEBO
        throw()
#endif
        {}

        const char* what() const
#ifndef __GAZEBO
        throw()
#endif
        {
            //return message_.cStr();
            return "DataElement not active";
        }

    private:
        //Str message_;
    };

    /// Defines an accuracy quantity which means "no accuracy specified"
    /// typically means "don't use" i.e. GPS without lock.
    static const float NO_ACCURACY;

    /// Destructor
    virtual ~DataElement();

    /// Returns the DataReader associated with this DataElement and the specified ElementURI
    DataReader* findReader( const ElementURI& uri );

    /// Returns the DataWriter associated with this DataElement and the specified ElementURI
    DataWriter* findWriter( const ElementURI& uri );

    /// Get the full ElementURI for the DataElement
    ///
    /// \return Reference to element's URI
    const ElementURI& getUri( void ) const;

    /// Returns the timestamp of last write
    virtual const Timestamp& getTimestamp() const
    {
        return timestamp_;
    };

    //-- Functions dealing with the element's "state" ---------------

    /// Is the element "unavailable"
    ///
    /// \return true if the element is marked as unavailable.
    virtual bool isUnavailable( void ) const;

    /// Is the element "invalid"
    ///
    /// \return true if the element is marked as invalid.
    virtual bool isInvalid( void ) const;

    virtual bool isOwnersFailed( void ) const;

    /// Set the element's unavailable flag.
    ///
    /// \param unavailable new value of unavailable flag
    /// \param notifier -- the writer that has alerted us to the condition
    /// \return New value of unavailable flag
    bool setUnavailable( bool unavailable, DataWriter* notifier );
    bool setUnavailable( bool unavailable, Component* notifier );

    /// Set the element's invalid flag.
    ///
    /// \param invalid new value of invalid flag
    /// \param notifier -- the writer that has alerted us to the condition
    /// \return New value of invalid flag
    bool setInvalid( bool invalid, DataWriter* notifier );
    bool setInvalid( bool invalid, Component* notifier );

    /// Sets the timestamp of last write
    /// Used if a more acurate timestamp is available
    virtual void setTimestamp( const Timestamp& timestamp = Timestamp::NOT_SET_TIME );

    /// Returns the value of the element's "orphand" flag.
    ///
    /// \return true if the element is considered orphaned
    virtual bool isOrphaned( void ) const;

    virtual bool isOwnersActive( void ) const;

    /// Indicates if the element is capable of being "active".
    /// At present, this is defined as the absence of any of the
    /// fault flags: orphaned, unavailable
    ///
    /// \return true if the element is neither orphaned nor unavailable.
    virtual bool isActivatable( void ) const;

    /// Indicates if the element is "active".
    /// At present, this means the element is activatable and its owner is active
    ///
    /// \return true if the element is neither orphaned nor unavailable.
    virtual bool isActive( void ) const;

    /// Indicates if the element is the "best" of a set of measurements
    /// that feed into a universal.  If this does not feed a universal
    /// returns false.
    ///
    /// \return true if the element is the "best" of a set of measurements
    /// that feed into a universal.
    bool isBest( DataAccessor::RequestStrategy strategy = DataAccessor::MIN_ERROR ) const;

    /// Returns the Unit associated with this data element
    /// or NULL if no Unit is associated
    const Unit* getUnit( void ) const;

    virtual bool isUniversal( ) const
    {
        return false;
    }

    //-- Registration functions -------------------------------------
    /// Indicates this DataElement should participate in the selection
    /// process for the given Universal.  It's up to the user to
    /// ensure this makes sense.
    ///
    /// \param abstract  The UniversalDataElement which this DataElement
    ///                  should forward any changes.
    void registerUniversal( UniversalDataElement *abstract );

    /// Returns the universal data element registered to this
    /// data element.
    UniversalDataElement* getUniversal( void ) const
    {
        return universal_;
    }

    /// Add the given DataReader to the list of readers for this DataElement
    ///
    /// \param reader DataReader to be added.
    void registerReader( DataReader *reader );

    /// Remove the given DataReader from the list of reader for this DataElement
    ///
    /// \param reader DataReader to be removed.
    void unregisterReader( DataReader *reader );

    /// The number of readers currently registered to this DataElement.
    ///
    /// \return The number of readers currently registered to this DataElement
    unsigned int readerCount() const;

    /// Does this DataElement have any readers?
    ///
    /// \return true if this DataElement has any registered readers.
    bool hasReaders( void ) const
    {
        return 0 < readerCount();
    }

    /// Returns true if one of the registered readers is requesting data
    bool isDataRequested();

    /// Returns true if there is a non-failed "implementor" reader of this element
    bool isImplemented();

    /// Add the given writer to the list of registered writers for this DataElement.
    ///
    /// \param writer Writer to register to this DataElement
    void registerWriter( DataWriter *writer );

    /// Remove the given writer from the list of registered writers for this DataElement.
    ///
    /// \param writer Writer to remove (disassociate) from this DataElement.
    void unregisterWriter( DataWriter *writer );

    /// Number of writers registered to this DataElement
    ///
    /// \return Number of writers registered to this DataElement
    unsigned int writerCount() const;

    /// Does this DataElement have any writers?
    ///
    /// \return true if this DataElement has any registered writers.
    bool hasWriters( void ) const
    {
        return 0 < writerCount();
    }

    //===============================================================
    // Logging functions
    virtual void registerRead( DataReader &reader );
    virtual void registerWrite( DataWriter &writer, bool changed );

    //===============================================================
    // Universal interactions
    void registerChangePending( DataWriter* writer );
    void registerChangeDone( Component* notifier );
    void registerChangeDone( void );

    //===============================================================
    ///\name Get the DataValue
    /// \{
    /// Basic get functions.
    virtual bool get( DataReader &reader, const Unit& unit, bool& readTo )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    virtual bool get( DataReader &reader, const Unit& unit, unsigned char& readTo )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    virtual bool get( DataReader &reader, const Unit& unit, double& readTo )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    virtual bool get( DataReader &reader, const Unit& unit, float& readTo )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    virtual bool get( DataReader &reader, const Unit& unit, int &readTo )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    virtual bool get( DataReader &reader, DataValue& readTo )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    /// \}

    /// \name "In-line" gets.
    /// \{
    virtual const double asDouble( DataReader &reader, const Unit& unit )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    virtual const int asInt( DataReader &reader, const Unit& unit )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    virtual const Str asString( DataReader &reader, const Unit& unit )
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
    ;
    /// \}

    /// Access to get the current accuracy.  Exact implementation
    /// will depend on the type of data element.
    /// \return current accuracy
    virtual float getAccuracy( const Unit& unit ) const
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
        = 0;

    /// Access to get the current accuracy.  Exact implementation
    /// will depend on the type of data element.
    /// \return current accuracy in SI units
    virtual float getAccuracySI() const
#ifndef __GAZEBO
    throw( UninitializedException )
#endif
        = 0;

    /// Returns the accuracy at the time of the last write;
    /// \return accuracy at the time of the last write.
    virtual float getWrittenAccuracy( const Unit& unit ) const
    {
        return unit.getScaled( writtenAccuracy_ );
    }

    //===============================================================
    ///\name Set a single value.
    ///@{
    /// Basic set functions.
    /// \param writer Writer which is setting the DataElement
    /// \param value DataValue to copy.
    /// \return true if set is successful.
    virtual bool set( DataWriter &writer, const Unit& unit, const bool value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool set( DataWriter &writer, const Unit& unit, const unsigned char value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool set( DataWriter &writer, const Unit& unit, const double value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool set( DataWriter &writer, const Unit& unit, const float value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool set( DataWriter &writer, const Unit& unit, const int value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool set( DataWriter &writer, const DataValue& value, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    ///@}

    /// \name Set a value and accuracy.
    ///@{
    /// Sets a value or array of values and the accuracy as a single action.
    /// \param writer Writer which is setting the value.
    /// \param value New value(s) being set.
    /// \param accuracy New accuracy value to use.
    /// \return true if set was successful
    virtual bool setWithAccuracy( DataWriter &writer, const Unit& unit, const bool value, const float accuracy, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool setWithAccuracy( DataWriter &writer, const Unit& unit, const unsigned char value, const float accuracy, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool setWithAccuracy( DataWriter &writer, const Unit& unit, const double value, const float accuracy, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool setWithAccuracy( DataWriter &writer, const Unit& unit, const int value, const float accuracy, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    virtual bool setWithAccuracy( DataWriter &writer, const DataValue& value, const float accuracy, const Timestamp& timestamp = Timestamp::NOT_SET_TIME );
    ///@}

    //===============================================================
    /// Set just the accuracy
    ///
    /// \param writer The DataWriter which is setting the accuracy
    /// \param unit The Unit in which the new accuracy value is expressed
    /// \param accuracy The new accuracy to use
    virtual bool setAccuracy( DataWriter &writer, const Unit& unit, const float accuracy );

    /// Returns the accuracy in base, unscaled SI units.
    virtual float getBaseAccuracy() const = 0;

    /// The base unit that all writers and readers must use when accessing this value
    virtual const Unit* getBaseUnit()
    {
        return baseUnit_;
    }

    /// The base size that all writers must specify
    virtual size_t getTypeSize()
    {
        return typeSize_;
    }

    /**
     * Returns true if the value was touched since the last run of the
     * indicated component.
     */
    virtual bool wasTouchedSinceLastRun( const Component* component );

    /// Power used by the component that owns this element
    float getPower()
    {
        return power_;
    }

    /// Error in measurements
    float getError()
    {
        return getBaseAccuracy();
    }

    /// Latency involved in requesting a measurement
    float getLatency()
    {
        return latency_;
    }

    /// Frequency of measurements possible
    float getFrequency()
    {
        return frequency_;
    }

    /// Power used by the component that owns this element
    void setPower( float power )
    {
        power_ = power;
    }

    /// Latency involved in requesting a measurement
    void setLatency( float latency )
    {
        latency_ = latency;
    }

    /// Frequency of measurements possible
    void setFrequency( float frequency )
    {
        frequency_ = frequency;
    }

    DataValue* getDataValueCopy( void ) const
    {
        DataValue* dv = getDataValue();
        return dv == NULL ? NULL : dv->copy();
    }

protected:

    /// Protected constructor for base class
    DataElement( const ElementURI& name, const Unit* baseUnit, size_t typeSize );

    /// Called before set commands to update the written accuracy
    /// Returns true if the written accuracy has changed.
    bool setWrittenAccuracy();

    ElementURI name_;                ///< The DataElement ElementURI.

    /// Queue of UniversalElements which this DataElement
    /// "participates in"
    //UniversalElementQueue universalQueue_;
    // Really just one UniversalElement.
    UniversalDataElement* universal_;

    /// The data writer responsable for notifying this element's
    /// Universal data element that a change is pending.
    DataWriter *notifier_;

    FlexArray<DataWriter*> writers_;            ///< List of all registered DataWriters.
    FlexArray<DataReader*> readers_;            ///< List of all registered DataReaders.

    /// The "unavailable" flag is actually a simple boolean which is set
    /// by the user or by the DataElement itself.
    bool unavailable_;

    /// The "invalid" flag is actually a simple boolean which is set
    /// by the user or by the DataElement itself.
    bool invalid_;

    /// Indicates that the element was previously orphaned
    bool wasOrphaned_;

    /// Mutex for thread-safing an instance of DataElement.
    Mutex mutex_;

    /// Accuracy of last written value
    float writtenAccuracy_;

    /// Data timestamp of last write
    Timestamp timestamp_;

    /// Timestamp of last write
    Timestamp touchtime_;

    /** Provides access to the an Element's DataValue.
     *  Implementation depends on the type of DataElement.
     *  \return Pointer to a valid DataValue
     */
    virtual DataValue* getDataValue( void ) const = 0;

    virtual void setDataValue( DataValue* dataValue ) = 0;

    /** Protected access to set the Element's accuracy.
     *  Implementation dependent on the type of data element.
     *  \param unit The unit of the accuracy value
     *  \param accuracy New accuracy to use
     *  \return The new accuracy
     */
    virtual float setAccuracy( const Unit& unit, float accuracy ) = 0;

    /// Flag -- false until the first call to set done.
    bool hasBeenSet_;

    /// The base unit that all writers and readers must use when accessing this value
    const Unit* baseUnit_;

    virtual void setBaseUnit( const Unit* baseUnit )
    {
        baseUnit_ = baseUnit;
    }

    /// The base size that all writers must specify
    size_t typeSize_;

    virtual void setTypeSize( const size_t typeSize )
    {
        typeSize_ = typeSize;
    }

    /// Power used by the component that owns this element
    float power_;

    /// Latency involved in requesting a measurement
    float latency_;

    /// Frequency of measurements possible
    float frequency_;

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

};

typedef FlexArray<DataElement*> DataElementList;

#endif /*DATAELEMENT_H_*/
