//  Copyright (c) 2001, Reson, Inc. All Rights Reserved.
//
//  Filename:   ChannelData.h
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      
//

#if !defined(AFX_CHANNELDATA_H__B2246F82_F0FC_4B36_B1E8_549C85AD2A57__INCLUDED_)
#define AFX_CHANNELDATA_H__B2246F82_F0FC_4B36_B1E8_549C85AD2A57__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "..\NetUtils\DynamicBuffer.h"

#include <list>
#include <algorithm>

#pragma pack( push, CHANNELDATA_H_PACK, 1 )

typedef struct tagQUEUEDMESSAGE
{
    // Attributes.

    int             m_iIndex;
    unsigned long   m_ulTimeStamp;

    // Methods.

    tagQUEUEDMESSAGE( void )
    {
        Reset();
    }

    virtual ~tagQUEUEDMESSAGE( void )
    {
        Reset();
    }

    virtual void Reset( void )
    {
        m_iIndex        = -1;
        m_ulTimeStamp   = 0UL;
    }

    bool IsValid( void ) const
    {
        return ( m_iIndex != -1 );
    }

    void SetValidIndex( const int &riIndex )
    {
        m_iIndex = riIndex;
    }

    bool operator < ( const tagQUEUEDMESSAGE &rRhs ) const
    {
        return ( m_iIndex < rRhs.m_iIndex );
    }

    bool operator == ( const int &riIndex ) const
    {
        // Operator used by std::find() to retrieve a object with specified index.

        return ( m_iIndex == riIndex );
    }
}
QUEUEDMESSAGE, *PQUEUEDMESSAGE;

typedef struct tagCHANNELITEM : public tagQUEUEDMESSAGE
{
    // Channel data attributes.

    unsigned long  m_ulChannel;
    unsigned long  m_ulSubsystem;
    unsigned long  m_ulPingNumber;

    // Basic helper methods for this struct.
                                
    tagCHANNELITEM( void ) : tagQUEUEDMESSAGE()
    {
        Reset();
    }

    ~tagCHANNELITEM( void )
    {
        Reset();
    }

    void Reset( void )
    {
        m_ulTimeStamp  = 0UL;
        m_ulChannel    = 0UL;
        m_ulSubsystem  = 0UL;
        m_ulPingNumber = 0UL;
    }

    bool operator < ( const tagCHANNELITEM &rRhs ) const
    {
        // Operator used by std::list to sort items of this type.
        // Note that the sort priority is:
        //  1)  Ping number
        //  2)  Subsystem number
        //  3   Channel number

        bool bIsLess = false;

        if ( m_ulPingNumber < rRhs.m_ulPingNumber )
        {
            bIsLess = true;
        }
        else if ( m_ulPingNumber == rRhs.m_ulPingNumber )
        {
            if ( m_ulSubsystem < rRhs.m_ulSubsystem )
            {
                bIsLess = true;
            }
            else if ( m_ulSubsystem == rRhs.m_ulSubsystem )
            {
                if ( m_ulChannel < rRhs.m_ulChannel )
                {
                    bIsLess = true;
                }
            }
        }

        return bIsLess;
    }
}
CHANNELITEM, *PCHANNELITEM;

#pragma pack( pop, CHANNELDATA_H_PACK )

template <typename tItem>
class CBufferedData
{
protected:

    typedef std::list<tItem>            ItemList_t;
    typedef ItemList_t::iterator        ItemListIterator_t;
    typedef ItemList_t::const_iterator  ConstItemListIterator_t;

    ///////////////
    // Attributes.

    const unsigned long                 m_ulMaxItems;

    bool                                m_bInitialized;

    ItemList_t                          m_ItemList;
    CDynamicBuffer<BYTE>               *m_pData;

    ///////////////
    // Services.

    void        Sort                (   void );

    int         NextAvailableItem   (   void );

    bool        GetItemIterator     (   const int               &riIndex,
                                        ItemListIterator_t      &rIterator,
                                        const bool              &rbValidate = true );

public:

    ///////////////
    // Definitions.

    typedef std::list<int>              IndexList_t;
    typedef IndexList_t::iterator       IndexListIterator_t;

    ///////////////
    // Attributes

    const int                           m_iInvalidIndex;

    ///////////////
    // Services.

                    CBufferedData       (   const unsigned long     &rulMaxItems = 100UL );
                                    
    virtual        ~CBufferedData       (   void );
                                    
    bool            IsIntiailzed        (   void ) const;

    bool            Add                 (   BYTE                    *pbyData,
                                            unsigned long           &rulNumBytes,
                                            tItem                   &rItem );
                                    
    bool            Remove              (   const int               &riIndex );

    bool            Get                 (   const int               &riIndex,
                                            tItem                  *&rpsItem,
                                            CDynamicBuffer<BYTE>   *&rpBuffer );
                                    
    bool            ValidItemList       (   IndexList_t             &rIndexList );
                                    
    void            Clear               (   void );


    unsigned long   Items               (   void ) const;

    int             Front               (   void ) const;

};

///////////////////////////////////////////////////////////////////////////////
// Public services of CBufferedData<tItem>

template <typename tItem>
CBufferedData<tItem>::CBufferedData( const unsigned long &rulMaxItems )

                     :m_ulMaxItems ( rulMaxItems ),
                      m_iInvalidIndex ( -1 )
{
    m_bInitialized = false;

    try
    {
        m_pData = new CDynamicBuffer<BYTE>[ m_ulMaxItems ];
        ASSERT( m_pData != NULL );

        Clear();
    }
    catch ( ... )
    {
        m_bInitialized = false;
    }

    m_bInitialized = true;
}

template <typename tItem>
CBufferedData<tItem>::~CBufferedData( void )
{
    m_bInitialized = false;

    if ( m_pData != NULL )
    {
        delete [] m_pData;
        m_pData = NULL;
    }

    m_ItemList.clear();
}

template <typename tItem>
bool CBufferedData<tItem>::IsIntiailzed ( void ) const
{
    return m_bInitialized;
}

template <typename tItem>
bool CBufferedData<tItem>::Remove( const int &riIndex )
{
    bool bRemoved = false;

    ItemListIterator_t pItem;

    if ( GetItemIterator( riIndex, pItem, false ) )
    {
        m_pData[ riIndex ].Reset();

        m_ItemList.erase( pItem );

        Sort();

        bRemoved = true;
    }

    return bRemoved;
}

template <typename tItem>
bool CBufferedData<tItem>::Add( BYTE            *pbyData,
                                unsigned long    &rulNumBytes,
                                tItem            &rItem )
{
    bool bSuccess = false;  // Assume failure for now.

    ASSERT( pbyData     != NULL );
    ASSERT( rulNumBytes >  0UL );

    if ( ( pbyData != NULL ) && ( rulNumBytes > 0UL ) )
    {
        int iIndex = NextAvailableItem();

        if ( iIndex != m_iInvalidIndex )
        {
            // Create the item object.

            rItem.SetValidIndex( iIndex );

            // Push it onto the info list.

            m_ItemList.push_back( rItem );

            // Copy the data to the corresponding channel buffer.

            m_pData[ iIndex ].Add( pbyData, rulNumBytes );

            // Sort the list in priority order ... see CHANNELITEM::operator < () for rules.

            Sort();

            bSuccess = true;
        }
    }

    return bSuccess;
}

template <typename tItem>
bool CBufferedData<tItem>::Get( const int               &riIndex,
                                tItem                  *&rpsItem,
                                CDynamicBuffer<BYTE>   *&rpBuffer )
{
    ItemListIterator_t pItem;

    if ( GetItemIterator( riIndex, pItem, true ) )
    {
        rpsItem  = (tItem *) (&(*pItem));
        rpBuffer = (CDynamicBuffer<BYTE> *) (&m_pData[ riIndex ]);
    }
    else
    {
        rpsItem = NULL;
        rpBuffer = NULL;
    }

    return ( (rpsItem != NULL ) && ( rpBuffer != NULL ) );
}

template <typename tItem>
unsigned long CBufferedData<tItem>::Items( void ) const
{
    return ( static_cast<unsigned long>(m_ItemList.size()) );
}

template <typename tItem>
int CBufferedData<tItem>::Front( void ) const
{
    // Returns the index of the first item in the list.

    int iIndex = m_iInvalidIndex;

    if ( ! m_ItemList.empty() )
    {
        const tItem &rItem = m_ItemList.front();

        if ( rItem.IsValid() )
        {
            iIndex = rItem.m_iIndex;
        }
    }

    return iIndex;
}

template <typename tItem>
bool CBufferedData<tItem>::ValidItemList( IndexList_t &rIndexList )
{
    rIndexList.clear();

    if ( ! m_ItemList.empty() )
    {
        // Build the list of valid idexes.

        for ( ItemListIterator_t pItem = m_ItemList.begin(); pItem != m_ItemList.end(); pItem++ )
        {
            if ( pItem->IsValid() )
            {
                rIndexList.push_back( pItem->m_iIndex );
            }
        }

        // Sort the list by index in ascending order.

        rIndexList.sort();
    }

    return ( ! rIndexList.empty() );
}

template <typename tItem>
void CBufferedData<tItem>::Clear( void )
{
    // Clear all items and their associated data.

    for ( unsigned long ulItem = 0UL; ulItem < m_ulMaxItems; ulItem++ )
    {
        m_pData[ ulItem ].Reset();
    }

    m_ItemList.clear();
}

///////////////////////////////////////////////////////////////////////////////
// Private services of CBufferedData<tItem>

template <typename tItem>
void CBufferedData<tItem>::Sort( void )
{
    if ( ! m_ItemList.empty() )
    {
        m_ItemList.sort();
    }
}

template <typename tItem>
int CBufferedData<tItem>::NextAvailableItem( void )
{
    // Scans the index list for the first available vacancy and return the index.

    int iIndex = m_iInvalidIndex;

    IndexList_t IndexList;

    if ( ValidItemList( IndexList ) )
    {
        // Search the list and look for the first available index discontinuity and use that otherwise,
        // just use 1 past the last item in the sorted list.

        IndexListIterator_t pIndex = IndexList.begin();

        while ( pIndex != IndexList.end() )
        {
            int iIndex1 = *pIndex;

            pIndex++;

            if ( pIndex != IndexList.end() )
            {
                if ( *pIndex > ( iIndex1 + 1 ) )
                {
                    iIndex = iIndex1 + 1;
                    break;
                }
            }
            else
            {
                iIndex = iIndex1 + 1;
                break;
            }
        }

        IndexList.clear();
    }
    else
    {
        iIndex = 0;
    }

    // Finally, check to see if index is in range; otherwise we risk writing beyond our 
    // dynamic buffer object array.

    if ( ( iIndex < 0 ) || 
         ( static_cast<unsigned long>( iIndex) >= m_ulMaxItems ) )
    {
        iIndex = m_iInvalidIndex;
    }

    return iIndex;
}

template <typename tItem>
bool CBufferedData<tItem>::GetItemIterator( const int             &riIndex,
                                            ItemListIterator_t    &rIterator,
                                            const bool            &rbValidate )
{
    bool bRetrieved = false;

    if ( ( rIterator = std::find( m_ItemList.begin(), m_ItemList.end(), riIndex ) ) != m_ItemList.end() )
    {
        bRetrieved = ( rbValidate ? rIterator->IsValid() : true );
    }

    return bRetrieved;
}

#endif // !defined(AFX_CHANNELDATA_H__B2246F82_F0FC_4B36_B1E8_549C85AD2A57__INCLUDED_)

