// MessageImpl.h
// Declaration of helper classes for implementing the IMessageMembers interface

#ifndef __MESSAGEIMPL_H
#define __MESSAGEIMPL_H

#include "Message.h"

template< class ImplT >
class ATL_NO_VTABLE IMessageIteratorImpl
: public IDispatchImpl< IMessageIterator, &__uuidof( IMessageIterator ), &LIBID_DecalNet >
{
public:
   enum { eEndIndex = 0xFFFFFFFF };

   cMessage *getSource();
   DWORD getStartIndex();
   DWORD getEndIndex();

   DWORD m_dwIterator;
   long m_nIndex;

   void init()
   {
      m_nIndex = -1;
   }

   DWORD getNextIndex( DWORD dwIndex )
   {
      DWORD dwNextIndex = dwIndex + ( static_cast< ImplT * >( this )->getSource()->m_fields.begin() + dwIndex )->m_nOwns;
      if( dwNextIndex == static_cast< ImplT * >( this )->getEndIndex() )
         return eEndIndex;
      return dwNextIndex;
   }

   DWORD getNextIndex()
   {
      if( m_nIndex == -1 )
      {
         DWORD dwIndex = static_cast< ImplT * >( this )->getStartIndex();
         if ( dwIndex == static_cast< ImplT * >( this )->getEndIndex () )
            // This was 0 size
            return eEndIndex;

         return dwIndex;
      }

      return static_cast< ImplT * >( this )->getNextIndex( m_dwIterator );
   }

   HRESULT advanceToIndex( long nIndex )
   {
      if( nIndex <= m_nIndex )
      {
         _ASSERT( FALSE );
         return E_INVALIDARG;
      }

      int nCurIndex = m_nIndex;
      for( DWORD dwIterator = static_cast< ImplT * >( this )->getNextIndex(); dwIterator != eEndIndex; dwIterator = static_cast< ImplT * >( this )->getNextIndex( dwIterator ) )
      {
         // Reset the counter
         ++ nCurIndex;
         if( nCurIndex == nIndex )
         {
            m_dwIterator = dwIterator;
            m_nIndex = nCurIndex;
            return S_OK;
         }
      }

      // The advance went off the edge
      m_dwIterator = eEndIndex;
      return S_FALSE;
   }

   HRESULT advanceToName( BSTR strName )
   {
      int nIndex = m_nIndex;
      for( DWORD dwIterator = static_cast< ImplT * >( this )->getNextIndex(); dwIterator != eEndIndex; dwIterator = static_cast< ImplT * >( this )->getNextIndex( dwIterator ) )
      {
         ++ nIndex;
         if( ::VarBstrCmp( strName, ( static_cast< ImplT * >( this )->getSource()->m_fields.begin() + dwIterator )->m_pSchema->m_strName, LOCALE_USER_DEFAULT, 0 ) == VARCMP_EQ )
         {
            // We've found the field
            m_nIndex = nIndex;
            m_dwIterator = dwIterator;
            return S_OK;
         }
      }

      return S_FALSE;
   }

   HRESULT advanceNext()
   {
      m_dwIterator = static_cast< ImplT * >( this )->getNextIndex();
      if( m_dwIterator == eEndIndex )
         return S_FALSE;

      ++ m_nIndex;
      return S_OK;
   }

   STDMETHOD(get_Current)(VARIANT *pData)
   {
      if( pData == NULL )
      {
         _ASSERT( FALSE );
         return E_POINTER;
      }

      if( m_dwIterator == eEndIndex )
      {
         pData->vt = VT_NULL;
         return E_FAIL;
      }

      cMessage::cFieldList::iterator i = static_cast< ImplT * >( this )->getSource()->m_fields.begin() + m_dwIterator;
      i->m_pSchema->getValue( static_cast< ImplT * >( this )->getSource(), i, pData );
      return S_OK;
   }

   STDMETHOD(get_MemberName)( BSTR *pstrName )
   {
      if( pstrName == NULL )
      {
         _ASSERT( FALSE );
         return E_POINTER;
      }

      if( m_dwIterator == eEndIndex )
         return E_FAIL;

      cMessage::cFieldList::iterator i = static_cast< ImplT * >( this )->getSource()->m_fields.begin() + m_dwIterator;
      *pstrName = SysAllocString( i->m_pSchema->m_strName );

      return S_OK;
   }

   STDMETHOD(get_Index)( long *pnIndex )
   {
      if( pnIndex == NULL )
      {
         _ASSERT( FALSE );
         return E_POINTER;
      }

      if( m_dwIterator == eEndIndex )
         return E_FAIL;

      *pnIndex = m_nIndex;
      return S_OK;
   }

   STDMETHOD(get_Next)( VARIANT vIndex, LPVARIANT pvValue )
   {
      if( pvValue == NULL )
      {
         _ASSERT( FALSE );
         return E_POINTER;
      }

      HRESULT hRes;
      if( vIndex.vt == VT_ERROR )
         // Error value indicates the optional value was not filled
         hRes = advanceNext();
      else if( vIndex.vt == VT_BSTR )
         hRes = advanceToName( vIndex.bstrVal );
      else
      {
         // Try and convert it to a long
         HRESULT hResConv = ::VariantChangeType( &vIndex, &vIndex, 0, VT_I4 );
         if( FAILED( hResConv ) )
         {
            _ASSERT( FALSE );
            return E_INVALIDARG;
         }

         hRes = advanceToIndex( vIndex.lVal );
      }

      if( hRes == S_FALSE )
      {
         pvValue->vt = VT_NULL;
         return hRes;
      }
         
      cMessage::cFieldList::iterator i = static_cast< ImplT * >( this )->getSource()->m_fields.begin() + m_dwIterator;
      i->m_pSchema->getValue( static_cast< ImplT * >( this )->getSource(), i, pvValue );
      return S_OK;
   }

   STDMETHOD(get_NextString)(BSTR Name, BSTR *pValue)
   {
      VARIANT v;
      v.vt = VT_BSTR;
      v.bstrVal = Name;

      VARIANT vOut;
      HRESULT hRes = get_Next( v, &vOut );

      if( hRes == S_FALSE )
         return E_FAIL;

      if( vOut.vt != VT_BSTR )
      {
         _ASSERT( FALSE );
         return E_FAIL;
      }

      *pValue = vOut.bstrVal;
      return S_OK;
   }

   STDMETHOD(get_NextInt)(BSTR Name, long *pValue)
   {
      VARIANT v;
      v.vt = VT_BSTR;
      v.bstrVal = Name;

      VARIANT vOut;
      HRESULT hRes = get_Next( v, &vOut );

      if( hRes == S_FALSE )
         return E_FAIL;

      if( vOut.vt == VT_I4 )
         *pValue = vOut.lVal;
      else if( vOut.vt == VT_I2 )
         *pValue = vOut.iVal;
      else if ( vOut.vt == VT_UI1 )
         *pValue = vOut.bVal;
      else
      {
         _ASSERT( FALSE );
         return E_FAIL;
      }

      return S_OK;
   }

   STDMETHOD(get_NextFloat)(BSTR Name, float *pValue)
   {
      VARIANT v;
      v.vt = VT_BSTR;
      v.bstrVal = Name;

      VARIANT vOut;
      HRESULT hRes = get_Next( v, &vOut );

      if( hRes == S_FALSE )
         return E_FAIL;

      if( vOut.vt == VT_R4 )
         *pValue = vOut.fltVal;
      else if( vOut.vt == VT_R8 )
         *pValue = static_cast< float >( vOut.dblVal );
      else
      {
         _ASSERT( FALSE );
         return E_FAIL;
      }

      return S_OK;
   }

   STDMETHOD(get_NextObject)(BSTR Name, IMessageIterator **pValue)
   {
      VARIANT v;
      v.vt = VT_BSTR;
      v.bstrVal = Name;

      VARIANT vOut;
      HRESULT hRes = get_Next( v, &vOut );

      if( hRes == S_FALSE )
         return E_FAIL;

      if( vOut.vt != VT_DISPATCH )
      {
         _ASSERT( FALSE );
         return E_FAIL;
      }

      return vOut.pdispVal->QueryInterface ( pValue );
   }

   STDMETHOD(get_NextObjectIndex)(IMessageIterator **pValue)
   {
      VARIANT v;
      v.vt = VT_ERROR;

      CComVariant vOut;
      HRESULT hRes = get_Next( v, &vOut );

      if( hRes == S_FALSE )
         return E_FAIL;

      if( vOut.vt != VT_DISPATCH )
      {
         _ASSERT( FALSE );
         return E_FAIL;
      }

      return vOut.pdispVal->QueryInterface ( pValue );
   }

   STDMETHOD(Reset)()
   {
      m_nIndex = -1;

      return S_OK;
   }

   STDMETHOD(get_Message)(IMessage **ppMessage)
   {
      if( ppMessage == NULL )
      {
         _ASSERT( FALSE );
         return E_POINTER;
      }

      static_cast< ImplT * >( this )->getSource()->QueryInterface( IID_IMessage, reinterpret_cast< void ** >( ppMessage ) );

      return S_OK;
   }
};

class IMessageIteratorSublistImpl
: public IMessageIteratorImpl< IMessageIteratorSublistImpl >
{
public:
   cMessage *m_pSource;
   DWORD m_dwStartIndex,
      m_dwEndIndex;

   cMessage *getSource()
   {
      return m_pSource;
   }

   DWORD getStartIndex()
   {
      return m_dwStartIndex;
   }

   DWORD getEndIndex()
   {
      return m_dwEndIndex;
   }

   void init( cMessage *pMessage, DWORD dwStructIndex )
   {
      m_pSource = pMessage;
      m_dwStartIndex = dwStructIndex + 1;
      m_dwEndIndex = dwStructIndex + ( m_pSource->m_fields.begin() + dwStructIndex )->m_nOwns;

      IMessageIteratorImpl< IMessageIteratorSublistImpl >::init();
   }
};

#endif
