/*===========================================================================
FILE:
   DB2Utilities.cpp

DESCRIPTION:
   Utility functions for packing/parsing protocol entities using the
   database

PUBLIC ENUMERATIONS AND METHODS:
   sProtocolEntityKey
   sDB2PackingInput
   sDB2NavInput

   MapQMIEntityTypeToProtocolType
   MapQMIEntityTypeToQMIServiceType
   MapQMIProtocolTypeToEntityType
   DB2GetMaxBufferSize
   DB2BuildQMIBuffer
   DB2PackQMIBuffer
   DB2ReduceQMIBuffer

Copyright (c) 2011, Code Aurora Forum. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Code Aurora Forum nor
      the names of its contributors may be used to endorse or promote
      products derived from this software without specific prior written
      permission.


THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
===========================================================================*/

//---------------------------------------------------------------------------
// Include Files
//---------------------------------------------------------------------------
#include "StdAfx.h"
#include "DB2Utilities.h"

#include "QMIBuffers.h"

#include "DataPacker.h"
#include "ProtocolEntityFieldEnumerator.h"

//---------------------------------------------------------------------------
// Definitions
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Free Methods
//---------------------------------------------------------------------------

/*===========================================================================
METHOD:
   MapQMIEntityTypeToQMIServiceType (Public Free Method)

DESCRIPTION:
   Map a DB protocol entity type (for QMI) to a QMI service type

PARAMETERS:
   et          [ I ] - Protocol entity type
  
RETURN VALUE:
   eQMIService
===========================================================================*/
eQMIService MapQMIEntityTypeToQMIServiceType( eDB2EntityType et )
{
   eQMIService st = eQMI_SVC_ENUM_BEGIN;
   switch (et)
   {
      case eDB2_ET_QMI_CTL_REQ:
      case eDB2_ET_QMI_CTL_RSP:
      case eDB2_ET_QMI_CTL_IND:
         st = eQMI_SVC_CONTROL;
         break;

      case eDB2_ET_QMI_WDS_REQ:
      case eDB2_ET_QMI_WDS_RSP:
      case eDB2_ET_QMI_WDS_IND:
         st = eQMI_SVC_WDS;
         break;

      case eDB2_ET_QMI_DMS_REQ:
      case eDB2_ET_QMI_DMS_RSP:
      case eDB2_ET_QMI_DMS_IND:
         st = eQMI_SVC_DMS;
         break;

      case eDB2_ET_QMI_NAS_REQ:
      case eDB2_ET_QMI_NAS_RSP:
      case eDB2_ET_QMI_NAS_IND:
         st = eQMI_SVC_NAS;
         break;

      case eDB2_ET_QMI_QOS_REQ:
      case eDB2_ET_QMI_QOS_RSP:
      case eDB2_ET_QMI_QOS_IND:
         st = eQMI_SVC_QOS;
         break;

      case eDB2_ET_QMI_WMS_REQ:
      case eDB2_ET_QMI_WMS_RSP:
      case eDB2_ET_QMI_WMS_IND:
         st = eQMI_SVC_WMS;
         break;

      case eDB2_ET_QMI_PDS_REQ:
      case eDB2_ET_QMI_PDS_RSP:
      case eDB2_ET_QMI_PDS_IND:
         st = eQMI_SVC_PDS;
         break;
      
      case eDB2_ET_QMI_AUTH_REQ:
      case eDB2_ET_QMI_AUTH_RSP:
      case eDB2_ET_QMI_AUTH_IND:
         st = eQMI_SVC_AUTH;
         break;

      case eDB2_ET_QMI_VOICE_REQ:
      case eDB2_ET_QMI_VOICE_RSP:
      case eDB2_ET_QMI_VOICE_IND:
         st = eQMI_SVC_VOICE;
         break;

      case eDB2_ET_QMI_CAT_REQ:
      case eDB2_ET_QMI_CAT_RSP:
      case eDB2_ET_QMI_CAT_IND:
         st = eQMI_SVC_CAT;
         break;

      case eDB2_ET_QMI_RMS_REQ:
      case eDB2_ET_QMI_RMS_RSP:
      case eDB2_ET_QMI_RMS_IND:
         st = eQMI_SVC_RMS;
         break;

      case eDB2_ET_QMI_OMA_REQ:
      case eDB2_ET_QMI_OMA_RSP:
      case eDB2_ET_QMI_OMA_IND:
         st = eQMI_SVC_OMA;
         break;
   }

   return st;
}

/*===========================================================================
METHOD:
   MapQMIEntityTypeToProtocolType (Public Free Method)

DESCRIPTION:
   Map a DB protocol entity type (for QMI) to a buffer protocol type

PARAMETERS:
   et          [ I ] - Protocol entity type
  
RETURN VALUE:
   eProtocolType
===========================================================================*/
eProtocolType MapQMIEntityTypeToProtocolType( eDB2EntityType et )
{
   eProtocolType pt = ePROTOCOL_ENUM_BEGIN;
   switch (et)
   {
      case eDB2_ET_QMI_WDS_REQ:
         pt = ePROTOCOL_QMI_WDS_TX;
         break;

      case eDB2_ET_QMI_WDS_RSP:
      case eDB2_ET_QMI_WDS_IND:
         pt = ePROTOCOL_QMI_WDS_RX;
         break;

      case eDB2_ET_QMI_DMS_REQ:
         pt = ePROTOCOL_QMI_DMS_TX;
         break;

      case eDB2_ET_QMI_DMS_RSP:
      case eDB2_ET_QMI_DMS_IND:
         pt = ePROTOCOL_QMI_DMS_RX;
         break;

      case eDB2_ET_QMI_NAS_REQ:
         pt = ePROTOCOL_QMI_NAS_TX;
         break;

      case eDB2_ET_QMI_NAS_RSP:
      case eDB2_ET_QMI_NAS_IND:
         pt = ePROTOCOL_QMI_NAS_RX;
         break;

      case eDB2_ET_QMI_QOS_REQ:
         pt = ePROTOCOL_QMI_QOS_TX;
         break;

      case eDB2_ET_QMI_QOS_RSP:
      case eDB2_ET_QMI_QOS_IND:
         pt = ePROTOCOL_QMI_QOS_RX;
         break;

      case eDB2_ET_QMI_WMS_REQ:
         pt = ePROTOCOL_QMI_WMS_TX;
         break;

      case eDB2_ET_QMI_WMS_RSP:
      case eDB2_ET_QMI_WMS_IND:
         pt = ePROTOCOL_QMI_WMS_RX;
         break;

      case eDB2_ET_QMI_PDS_REQ:
         pt = ePROTOCOL_QMI_PDS_TX;
         break;

      case eDB2_ET_QMI_PDS_RSP:
      case eDB2_ET_QMI_PDS_IND:
         pt = ePROTOCOL_QMI_PDS_RX;
         break;

      case eDB2_ET_QMI_AUTH_REQ:
         pt = ePROTOCOL_QMI_AUTH_TX;
         break;

      case eDB2_ET_QMI_AUTH_RSP:
      case eDB2_ET_QMI_AUTH_IND:
         pt = ePROTOCOL_QMI_AUTH_RX;
         break;

      case eDB2_ET_QMI_VOICE_REQ:
         pt = ePROTOCOL_QMI_VOICE_TX;
         break;

      case eDB2_ET_QMI_VOICE_RSP:
      case eDB2_ET_QMI_VOICE_IND:
         pt = ePROTOCOL_QMI_VOICE_RX;
         break;

      case eDB2_ET_QMI_CAT_REQ:
         pt = ePROTOCOL_QMI_CAT_TX;
         break;

      case eDB2_ET_QMI_CAT_RSP:
      case eDB2_ET_QMI_CAT_IND:
         pt = ePROTOCOL_QMI_CAT_RX;
         break;

      case eDB2_ET_QMI_RMS_REQ:
         pt = ePROTOCOL_QMI_RMS_TX;
         break;

      case eDB2_ET_QMI_RMS_RSP:
      case eDB2_ET_QMI_RMS_IND:
         pt = ePROTOCOL_QMI_RMS_RX;
         break;

      case eDB2_ET_QMI_OMA_REQ:
         pt = ePROTOCOL_QMI_OMA_TX;
         break;

      case eDB2_ET_QMI_OMA_RSP:
      case eDB2_ET_QMI_OMA_IND:
         pt = ePROTOCOL_QMI_OMA_RX;
         break;

      case eDB2_ET_QMI_CTL_REQ:
         pt = ePROTOCOL_QMI_CTL_TX;
         break;

      case eDB2_ET_QMI_CTL_RSP:
      case eDB2_ET_QMI_CTL_IND:
         pt = ePROTOCOL_QMI_CTL_RX;
         break;
   }

   return pt;
}

/*===========================================================================
METHOD:
   MapQMIProtocolTypeToEntityType (Public Free Method)

DESCRIPTION:
   Map a buffer protocol type to a DB protocol entity type

PARAMETERS:
   pt          [ I ] - Protocol type
   bIndication [ I ] - Is this for an indication?
  
RETURN VALUE:
   eDB2EntityType
===========================================================================*/
eDB2EntityType MapQMIProtocolTypeToEntityType( 
   eProtocolType              pt,
   bool                       bIndication )
{
   eDB2EntityType et = eDB2_ET_ENUM_BEGIN;
   switch (pt)
   {
      case ePROTOCOL_QMI_WDS_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_WDS_IND;
         }
         else
         {
            et = eDB2_ET_QMI_WDS_RSP;
         }
         break;

      case ePROTOCOL_QMI_WDS_TX:
         et = eDB2_ET_QMI_WDS_REQ;
         break;

      case ePROTOCOL_QMI_DMS_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_DMS_IND;
         }
         else
         {
            et = eDB2_ET_QMI_DMS_RSP;
         }
         break;

      case ePROTOCOL_QMI_DMS_TX:
         et = eDB2_ET_QMI_DMS_REQ;
         break;

      case ePROTOCOL_QMI_NAS_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_NAS_IND;
         }
         else
         {
            et = eDB2_ET_QMI_NAS_RSP;
         }
         break;

      case ePROTOCOL_QMI_NAS_TX:
         et = eDB2_ET_QMI_NAS_REQ;
         break;

      case ePROTOCOL_QMI_QOS_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_QOS_IND;
         }
         else
         {
            et = eDB2_ET_QMI_QOS_RSP;
         }
         break;

      case ePROTOCOL_QMI_QOS_TX:
         et = eDB2_ET_QMI_QOS_REQ;
         break;

      case ePROTOCOL_QMI_WMS_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_WMS_IND;
         }
         else
         {
            et = eDB2_ET_QMI_WMS_RSP;
         }
         break;

      case ePROTOCOL_QMI_WMS_TX:
         et = eDB2_ET_QMI_WMS_REQ;
         break;

      case ePROTOCOL_QMI_PDS_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_PDS_IND;
         }
         else
         {
            et = eDB2_ET_QMI_PDS_RSP;
         }
         break;

      case ePROTOCOL_QMI_PDS_TX:
         et = eDB2_ET_QMI_PDS_REQ;
         break;

               case ePROTOCOL_QMI_AUTH_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_AUTH_IND;
         }
         else
         {
            et = eDB2_ET_QMI_AUTH_RSP;
         }
         break;

      case ePROTOCOL_QMI_AUTH_TX:
         et = eDB2_ET_QMI_AUTH_REQ;
         break;

      case ePROTOCOL_QMI_VOICE_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_VOICE_IND;
         }
         else
         {
            et = eDB2_ET_QMI_VOICE_RSP;
         }
         break;

      case ePROTOCOL_QMI_VOICE_TX:
         et = eDB2_ET_QMI_VOICE_REQ;
         break;

      case ePROTOCOL_QMI_CAT_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_CAT_IND;
         }
         else
         {
            et = eDB2_ET_QMI_CAT_RSP;
         }
         break;

      case ePROTOCOL_QMI_CAT_TX:
         et = eDB2_ET_QMI_CAT_REQ;
         break;

      case ePROTOCOL_QMI_RMS_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_RMS_IND;
         }
         else
         {
            et = eDB2_ET_QMI_RMS_RSP;
         }
         break;

      case ePROTOCOL_QMI_RMS_TX:
         et = eDB2_ET_QMI_RMS_REQ;
         break;

      case ePROTOCOL_QMI_OMA_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_OMA_IND;
         }
         else
         {
            et = eDB2_ET_QMI_OMA_RSP;
         }
         break;

      case ePROTOCOL_QMI_OMA_TX:
         et = eDB2_ET_QMI_OMA_REQ;
         break;

      case ePROTOCOL_QMI_CTL_RX:
         if (bIndication == true)
         {
            et = eDB2_ET_QMI_CTL_IND;
         }
         else
         {
            et = eDB2_ET_QMI_CTL_RSP;
         }
         break;

      case ePROTOCOL_QMI_CTL_TX:
         et = eDB2_ET_QMI_CTL_REQ;
         break;
   }

   return et;
}

/*===========================================================================
METHOD:
   DB2GetMaxBufferSize (Public Free Method)

DESCRIPTION:
   Return the maximum size of a payload buffer for given type of 
   protocol entity

PARAMETERS:
   et          [ I ] - Protocol entity type
  
RETURN VALUE:
   ULONG - Maximum 
===========================================================================*/
ULONG DB2GetMaxBufferSize( eDB2EntityType et )
{
   ULONG maxSzInBytes = MAX_SHARED_BUFFER_SIZE;

   if (IsQMIEntityType( et ) == true)
   {
      // QMI items are further constrained in size
      maxSzInBytes = QMI_MAX_BUFFER_SIZE;
   }

   return maxSzInBytes;
}

/*===========================================================================
METHOD:
   DB2BuildQMIBuffer (Internal Method)

DESCRIPTION:
   Build and return an allocated shared buffer for the QMI protocol using 
   the specified DB keys and payloads (one per TLV content)

PARAMETERS:
   input       [ I ] - Protocol entity key and payload
  
RETURN VALUE:
   sSharedBuffer * (0 upon failure)
===========================================================================*/
sSharedBuffer * DB2BuildQMIBuffer(
   const std::vector <sDB2NavInput> &  input )
{
   // Assume failure
   sSharedBuffer * pRef = 0;

   const ULONG szTransHdr = (ULONG)sizeof(sQMIServiceRawTransactionHeader);
   const ULONG szMsgHdr   = (ULONG)sizeof(sQMIRawMessageHeader);
   const ULONG szTLVHdr   = (ULONG)sizeof(sQMIRawContentHeader);

   // Need something to build
   ULONG tlvs = (ULONG)input.size();
   if (tlvs == 0)
   {
      return pRef;
   }

   // The protocol entity keys need to be consistent
   const sDB2NavInput & tlvInput = input[0];
   if (tlvInput.IsValid() == false || tlvInput.mKey.size() < 3)
   {
      return pRef;
   }

   eDB2EntityType et = (eDB2EntityType)tlvInput.mKey[0];
   if (IsQMIEntityType( et ) == false)
   {
      return pRef;
   }

   ULONG szReq = szTransHdr + szMsgHdr + (tlvs * szTLVHdr);
   szReq += tlvInput.mPayloadLen;

   ULONG t = 0;
   for (t = 1; t < tlvs; t++)
   {
      const sDB2NavInput & tlv2Input = input[t];
      if (tlv2Input.IsValid() == false || tlv2Input.mKey.size() < 3)
      {
         return pRef;
      }

      if ( (tlvInput.mKey[0] != tlv2Input.mKey[0])
      ||   (tlvInput.mKey[1] != tlv2Input.mKey[1]) )
      {
         return pRef;
      }

      szReq += tlv2Input.mPayloadLen;
   }

   // What we are building cannot be too large
   if (szReq > QMI_MAX_BUFFER_SIZE)
   {
      return pRef;
   }

   BYTE buf[QMI_MAX_BUFFER_SIZE];

   sQMIRawContentHeader * pTLV = 0;
   pTLV = (sQMIRawContentHeader *)&buf[0];

   for (t = 0; t < tlvs; t++)
   {
      const sDB2NavInput & tlv2Input = input[t];

      pTLV->mTypeID = (BYTE)tlv2Input.mKey[2];
      pTLV->mLength = (WORD)tlv2Input.mPayloadLen;
      pTLV++;

      const BYTE * pPayload = (const BYTE *)pTLV;
      memcpy( (LPVOID)pPayload, 
              (LPCVOID)tlv2Input.mpPayload, 
              (SIZE_T)tlv2Input.mPayloadLen );

      pPayload += tlv2Input.mPayloadLen;
      pTLV = (sQMIRawContentHeader *)pPayload;
   }

   ULONG contentLen = szReq - szTransHdr - szMsgHdr;
   eQMIService st = MapQMIEntityTypeToQMIServiceType( et );

   pRef = sQMIServiceBuffer::BuildBuffer( st,
                                          (WORD)tlvInput.mKey[1],
                                          IsQMIEntityResponseType( et ),
                                          IsQMIEntityIndicationType( et ),
                                          &buf[0],
                                          contentLen );

   return pRef;
}

/*===========================================================================
METHOD:
   DB2PackQMIBuffer (Internal Method)

DESCRIPTION:
   Build an allocated shared buffer for the QMI protocol

PARAMETERS:
   db          [ I ] - Database to use for packing
   input       [ I ] - Protocol entity key and field values
  
RETURN VALUE:
   sSharedBuffer * (0 upon failure)
===========================================================================*/
sSharedBuffer * DB2PackQMIBuffer(
   const cCoreDatabase &                  db,
   const std::vector <sDB2PackingInput> & input )
{
   // Assume failure
   sSharedBuffer * pRef = 0;

   // Need something to build
   ULONG tlvs = (ULONG)input.size();
   if (tlvs == 0)
   {
      return pRef;
   }

   // The protocol entity keys need to be consistent
   const sDB2PackingInput & tlvInput = input[0];
   if (tlvInput.IsValid() == false || tlvInput.mKey.size() < 3)
   {
      return pRef;
   }

   eDB2EntityType et = (eDB2EntityType)tlvInput.mKey[0];
   if (IsQMIEntityType( et ) == false)
   {
      return pRef;
   }

   ULONG t = 0;
   for (t = 1; t < tlvs; t++)
   {
      const sDB2PackingInput & tlv2Input = input[t];
      if (tlv2Input.IsValid() == false || tlv2Input.mKey.size() < 3)
      {
         return pRef;
      }

      if ( (tlvInput.mKey[0] != tlv2Input.mKey[0])
      ||   (tlvInput.mKey[1] != tlv2Input.mKey[1]) )
      {
         return pRef;
      }
   }

   BYTE buf[QMI_MAX_BUFFER_SIZE];
   ULONG bufLen = 0;

   sQMIRawContentHeader * pTLV = 0;
   pTLV = (sQMIRawContentHeader *)&buf[0];

   bool bOK = true;
   for (t = 0; t < tlvs; t++)
   {
      ULONG packedLen = 0;
      const BYTE * pPackedData = 0;

      const sDB2PackingInput & tlv2Input = input[t];

      if (tlv2Input.mbString == true)
      {
         if (tlv2Input.mValues.empty() == false)
         {
            // Convert field string to input fields
            std::list <sUnpackedField> fields 
               = cDataPacker::LoadValues( tlv2Input.mValues );

            // Now pack
            cDataPacker dp( db, tlv2Input.mKey, fields );
            bOK = dp.Pack();
            if (bOK == false)
            {
               break;
            }

            pPackedData = dp.GetBuffer( packedLen );
            if (pPackedData == 0)
            {
               bOK = false;
               break;
            }
         }
      }
      else
      {
         packedLen = tlv2Input.mDataLen;
         pPackedData = tlv2Input.mpData;
      }

      // Check if we need to adjust buffer
      cProtocolEntityFieldEnumerator pefe( db, tlv2Input.mKey );
      bool bEnum = pefe.Enumerate();
      if (bEnum == true)
      {
         const std::vector <ULONG> & fieldIDs = pefe.GetFields();
         ULONG fieldCount = (ULONG)fieldIDs.size();
         if (fieldCount == 1)
         {
            const tDB2FieldMap & dbFields = db.GetProtocolFields();

            tDB2FieldMap::const_iterator pField = dbFields.find( fieldIDs[0] );
            if (pField != dbFields.end())
            {
               const sDB2Field & theField = pField->second;
               if ( (theField.mType == eDB2_FIELD_STD)
               &&   (theField.mTypeVal == (ULONG)eDB2_FIELD_STDTYPE_STRING_ANT) )
               {
                  // For QMI we need to strip out the trailing NULL 
                  // string terminator when the TLV consists solely
                  // of a string since the length contained in the 
                  // TLV structure itself renders the trailing NULL 
                  // redundant
                  if (packedLen > 2)
                  {
                     packedLen--;
                  }
                  else 
                  {
                     // This is the only way to specify an empty string in QMI
                     // when the TLV consists solely of a string
                     if (packedLen == 1)
                     {
                        packedLen--;
                     }
                  }
               }
            }
         }
      }

      if (bufLen + (ULONG)sizeof(sQMIRawContentHeader) < bufLen)
      {
         bOK = false;
         break;
      }
      bufLen += (ULONG)sizeof(sQMIRawContentHeader);
      if (bufLen + packedLen < bufLen)
      {
         bOK = false;
         break;
      }
      bufLen += packedLen;

      // What we are building cannot be too large
      if (bufLen > QMI_MAX_BUFFER_SIZE)
      {
         bOK = false;
         break;
      }

      pTLV->mTypeID = (BYTE)tlv2Input.mKey[2];
      pTLV->mLength = (WORD)packedLen;
      pTLV++;

      const BYTE * pPayload = (const BYTE *)pTLV;
      memcpy( (LPVOID)pPayload, 
              (LPCVOID)pPackedData, 
              (SIZE_T)packedLen );

      pPayload += packedLen;
      pTLV = (sQMIRawContentHeader *)pPayload;
   }

   if (bOK == false)
   {
      return pRef;
   }

   eQMIService st = MapQMIEntityTypeToQMIServiceType( et );
   pRef = sQMIServiceBuffer::BuildBuffer( st,
                                          (WORD)tlvInput.mKey[1],
                                          IsQMIEntityResponseType( et ),
                                          IsQMIEntityIndicationType( et ),
                                          &buf[0],
                                          bufLen );

   return pRef;
}

/*===========================================================================
METHOD:
   DB2ReduceQMIBuffer (Public Free Method)

DESCRIPTION:
   Reduce a DIAG buffer to a DB key and payload

PARAMETERS:
   buf         [ I ] - Protocol buffer being reduced
  
RETURN VALUE:
   sDB2NavInput (invalid upon failure)
===========================================================================*/ 
std::vector <sDB2NavInput> DB2ReduceQMIBuffer( const sProtocolBuffer & buf )
{
   std::vector <sDB2NavInput> retInput;

   // We must have a valid protocol buffer
   if (buf.IsValid() == false)
   {
      return retInput;
   }

   eProtocolType pt = (eProtocolType)buf.GetType();
   if (IsQMIProtocol( pt ) != true)
   {
      return retInput;
   }

   sQMIServiceBuffer qmiBuf( buf.GetSharedBuffer() );
   std::map <ULONG, const sQMIRawContentHeader *> tlvs = qmiBuf.GetContents();

   bool bErr = false;
   std::map <ULONG, const sQMIRawContentHeader *>::const_iterator pIter;
   for (pIter = tlvs.begin(); pIter != tlvs.end(); pIter++)
   {
      const sQMIRawContentHeader * pHdr = pIter->second;
      if (pHdr == 0)
      {
         bErr = true;
         break;
      }

      bool bIndication = qmiBuf.IsIndication();
      eProtocolType pt = (eProtocolType)qmiBuf.GetType();
      eDB2EntityType et = MapQMIProtocolTypeToEntityType( pt, bIndication );

      sDB2NavInput tmp;
      tmp.mKey.push_back( (ULONG)et );
      tmp.mKey.push_back( qmiBuf.GetMessageID() );
      tmp.mKey.push_back( (ULONG)pHdr->mTypeID );
      
      tmp.mPayloadLen = pHdr->mLength;
      pHdr++;

      tmp.mpPayload = (const BYTE *)pHdr;
      if (tmp.IsValid() == true)
      {
         retInput.push_back( tmp );
      }
      else
      {
         // Ignore empty TLVs
         if (tmp.mPayloadLen != 0)
         {
            bErr = true;
            break;
         }
      }
   }

   if (bErr == true)
   {
      retInput.clear();
   }

   return retInput;
}
