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

#ifndef EVENTENTRY_H_
#define EVENTENTRY_H_

#include "LogEntry.h"

#include "component/FailureMode.h"
#include "utils/Mutex.h"

class Component;
class DataElement;
class RingBufferVoid;

/**
 *  Code unit that represents an event to be stored in the log file.
 *  The kinds of events include:
 *  \li Starting a computation cycle
 *  \li Setting the accuracy of a variable
 *  \li Setting the state of a variable
 *  \li Setting the "Best" value of a UniversalDataElement
 *
 * An EventEntry is serialized as:
 * - a 16-bit header (serialized using OutStream::writeUShortCompact) including:
 * 	 \li 12 high bits: Actor code (which can be used to look up
 *       what code unit is causing the event), or zero if this is the start
 *       of a cycle
 *   \li 2 bits: EventEntry::Action code, indicating the type of action being
 *       logged; e.g., the start of a cycle, the setting of accuracy, the setting
 *       of the state of a variable, or the setting of a variable to the "best"
 *       for its associated Universal variable
 *   \li 2 bits: the code 00, indicating this is a EventEntry
 * - Unless the action is EventEntry::START_CYCLE, 16 bits  (serialized using
 *   OutStream::writeUShortCompact) indicating the DataAccessor code of the
 *   DataElement undergoing the event.
 * - If the action is EventEntry::START_CYCLE, a 64-bit Timestamp as integer
 *   microseconds since the start of the epoch.  Otherwise, a 64-bit Timespan
 *   as integer microseconds since the previous LogEntry. (In both cases,
 *   serialized using OutStream::writeLongLongCompact).
 * - If the action is EventEntry::SET_DATA_ACCURACY, a 16-bit floating point number
 *   (serialized using OutStream::writeFloat2) indicating the new accuracy of
 *   the DataElement.  Or if the action is EventEntry::SET_DATA_STATE, a 8-bit
 *   ORed result of the EventEntry::StateChange flags for the DataElement.
 *
 *  \ingroup logging
 */
class EventEntry : public LogEntry
{
public:

    /// Type of data event
    typedef enum
    {
        START_CYCLE = 0,            /// Start of a cycle
        SET_DATA_ACCURACY = 1 << 2, /// Change of Data Accuracy
        SET_DATA_STATE = 2 << 2,    /// Indicate change to/from best, failed, orphaned, etc
        COMPONENT_ACTION = 3 << 2,  /// Indicate change to a component
    } EventType;
    static const int EVENT_TYPE_MASK = 3 << 2;

    typedef enum
    {
        NO_DATA_STATE_CHANGE = 0,
        DATA_STATE_CHANGE_UNAVAILABLE = 2,
        DATA_STATE_CHANGE_AVAILABLE = 4,
        DATA_STATE_CHANGE_ORPHANED = 8,
        DATA_STATE_CHANGE_UNORPHANED = 16,
        DATA_STATE_CHANGE_SET_BEST = 32,  /// Indicate change to the "best" of associated universal
        DATA_STATE_CHANGE_INVALID = 64,
        DATA_STATE_CHANGE_VALID = 128,
    } DataStateChange;

    /// Type of component event
    typedef enum
    {
        COMPONENT_LOAD = 0,             /// Followed by boolean true or false to indicate load/unload
        COMPONENT_MISSION_RUN = 1 << 2, /// Followed by 0=done, positive=loop#, negative=preempted in loop#
        COMPONENT_FAIL = 2 << 2,        /// Followed by failtype and failcount
    } ComponentAction;
    static const int COMPONENT_ACTION_MASK = 3 << 2;

    /// EventType Constructor
    EventEntry( Component* actor,
                DataElement* dataElement,
                const EventType eventType );

    /// DataStateChange Constructor
    EventEntry( Component* actor,
                DataElement* dataElement,
                const DataStateChange dataStateChange );

    /// ComponentAction = COMPONENT_LOAD Constructor
    EventEntry( Component* actor,
                const bool loaded );

    /// ComponentAction = COMPONENT_RUN Constructor
    EventEntry( Component* actor,
                const int loop );

    /// ComponentAction = COMPONENT_FAIL Constructor
    EventEntry( Component* actor,
                const FailureMode::FailType failure,
                const int failCount );

    /// Destructor
    virtual ~EventEntry();

    /// Override new
    void* operator new( size_t size );

    /// Override delete
    void operator delete( void* p );

    const void setAccuracy( const double& accuracy )
    {
        accuracy_ = accuracy;
    }

    const double& getAccuracy( void ) const
    {
        return accuracy_;
    }

    /** Read new EventEntry from a stream.
     *
     *  Implements requirement: \ref req_timing_accurateStorage
     *
     */
    static EventEntry* Read( Timestamp& timeBase, Timespan& timeAdjust, InStream &inStream, unsigned short firstWord, Logger& logger, bool& error );

    /** Send payload to a stream
     *
     *  Implements requirement: \ref req_timing_accurateStorage
     *
     */
    virtual unsigned int write( Timestamp& timeBase, OutStream& logStream ) const;

    virtual Str toString( const Unit* unit = NULL ) const;

    /// Convert actions to Strs
    static const char* EventTypeToString( EventType theAction );

    /// Convert actions to Strs
    static const char* StateChangeToString( DataStateChange dataStateChange );

    unsigned short getActorCode() const
    {
        return actorCode_;
    }

    ComponentAction getComponentAction( void ) const
    {
        return componentAction_;
    }

    unsigned short getDataElementCode( void ) const
    {
        return dataElementCode_;
    }

    EventType getEventType( void ) const
    {
        return eventType_;
    }

    DataStateChange getDataStateChange( void ) const
    {
        return dataStateChange_;
    }

    void setDataStateChange( const DataStateChange dataStateChange )
    {
        dataStateChange_ = dataStateChange;
    }

    int getRunLoop() const
    {
        return runLoop_;
    }

    bool getLoaded() const
    {
        return loaded_;
    }

    FailureMode::FailType getFailureMode() const
    {
        return failureMode_;
    }

    int getFailCount() const
    {
        return failCount_;
    }

protected:

    /// Protected EventType Constructor
    EventEntry( const Timestamp& timestamp,
                const unsigned short actorCode,       // Arg1
                const unsigned short dataElementCode, // Arg2
                const EventType action,               // Arg3
                const double& accuracy = 0.0 );       // Arg5

    /// Protected DataStateChange Constructor
    EventEntry( const Timestamp& timestamp,
                const unsigned short actorCode,        // Arg1
                const unsigned short dataElementCode,  // Arg2
                const DataStateChange dataStateChange, // Arg4
                const double& accuracy = 0.0 );        // Arg5

    /// Protected ComponentAction = COMPONENT_LOAD Constructor
    EventEntry( const Timestamp& timestamp,
                const unsigned short actorCode,        // Arg1
                const bool loaded );                   // Arg2

    /// Protected ComponentAction = COMPONENT_RUN Constructor
    EventEntry( const Timestamp& timestamp,
                const unsigned short actorCode,        // Arg1
                const int loop );                      // Arg2

    /// Protected ComponentAction = COMPONENT_FAIL Constructor
    EventEntry( const Timestamp& timestamp,
                const unsigned short actorCode,	      // Arg1
                const FailureMode::FailType failure,  // Arg2
                const int failCount );                // Arg5

    /// Arg1
    unsigned short actorCode_;

    /// Arg2
    union
    {
        unsigned short dataElementCode_;
        bool loaded_;
        int runLoop_;
        FailureMode::FailType failureMode_;
    };

    /// Arg3
    EventType eventType_;

    /// Arg4
    union
    {
        DataStateChange dataStateChange_;
        ComponentAction componentAction_;
    };

    /// Arg5
    union
    {
        double accuracy_;
        int failCount_;
    };

    virtual const Str makeComponentActionString() const;

    virtual const Str makeDataValueString() const;

    /// Declared as a pointer to avoid including RingBuffer.h header here.
    static RingBufferVoid* MallocRing_;

    /// Makes sure that mallocRing_s is deleted on shutdown
    class StaticDestructor
    {
    public:
        ~StaticDestructor();
    };

    /// Static instance of StaticDestructor
    static EventEntry::StaticDestructor StaticDestructor_;

    /// Used for free/delete operations
    static Mutex Mutex_;
};

#endif /*EVENTENTRY_H_*/
