/*
 * FreeRTOS MQTT V2.1.1
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/**
 * @file iot_ble_mqtt_serialize.c
 * @brief MQTT library for BLE.
 */
/* The config header is always included first. */
#include "iot_config.h"

/* Standard includes. */
#include <string.h>
#include <stdio.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"

#include "iot_ble_config.h"


/* MQTT internal includes. */
#include "platform/iot_threads.h"
#include "iot_serializer.h"
#include "platform/iot_network_ble.h"
#include "iot_ble_data_transfer.h"
#include "iot_ble_mqtt_serialize.h"
#include "private/iot_mqtt_internal.h"

#define _INVALID_MQTT_PACKET_TYPE    ( 0xF0 )


#define _IS_VALID_SERIALIZER_RET( ret, pSerializerBuf ) \
    ( ( ret == IOT_SERIALIZER_SUCCESS ) ||              \
      ( ( !pSerializerBuf ) && ( ret == IOT_SERIALIZER_BUFFER_TOO_SMALL ) ) )

#define _NUM_CONNECT_PARMAS            ( 4 )
#define _NUM_DEFAULT_PUBLISH_PARMAS    ( 4 )
#define _NUM_PUBACK_PARMAS             ( 2 )
#define _NUM_SUBACK_PARAMS             ( 4 )
#define _NUM_UNSUBACK_PARAMS           ( 3 )
#define _NUM_DISCONNECT_PARAMS         ( 1 )
#define _NUM_PINGREQUEST_PARAMS        ( 1 )

const IotMqttSerializer_t IotBleMqttSerializer =
{
    .serialize.connect       = IotBleMqtt_SerializeConnect,
    .serialize.publish       = IotBleMqtt_SerializePublish,
    .serialize.publishSetDup = IotBleMqtt_PublishSetDup,
    .serialize.puback        = IotBleMqtt_SerializePuback,
    .serialize.subscribe     = IotBleMqtt_SerializeSubscribe,
    .serialize.unsubscribe   = IotBleMqtt_SerializeUnsubscribe,
    .serialize.pingreq       = IotBleMqtt_SerializePingreq,
    .serialize.disconnect    = IotBleMqtt_SerializeDisconnect,
    .freePacket              = IotBleMqtt_FreePacket,
    .getPacketType           = IotBleMqtt_GetPacketType,
    .getRemainingLength      = IotBleMqtt_GetRemainingLength,
    .deserialize.connack     = IotBleMqtt_DeserializeConnack,
    .deserialize.publish     = IotBleMqtt_DeserializePublish,
    .deserialize.puback      = IotBleMqtt_DeserializePuback,
    .deserialize.suback      = IotBleMqtt_DeserializeSuback,
    .deserialize.unsuback    = IotBleMqtt_DeserializeUnsuback,
    .deserialize.pingresp    = IotBleMqtt_DeserializePingresp
};

/**
 * @brief Guards access to the packet identifier counter.
 *
 * Each packet should have a unique packet identifier. This mutex ensures that only
 * one thread at a time may read the global packet identifer.
 */


/**
 * @brief Generates a monotonically increasing identifier used in  MQTT message
 *
 * @return Identifier for the MQTT message
 */
static uint16_t _nextPacketIdentifier( void );


static inline uint16_t _getNumPublishParams( const IotMqttPublishInfo_t * const pPublish )
{
    return ( pPublish->qos > 0 ) ? ( _NUM_DEFAULT_PUBLISH_PARMAS + 1 ) : _NUM_DEFAULT_PUBLISH_PARMAS;
}

static IotSerializerError_t _serializeConnect( const IotMqttConnectInfo_t * const pConnectInfo,
                                               uint8_t * const pBuffer,
                                               size_t * const pSize );
static IotSerializerError_t _serializePublish( const IotMqttPublishInfo_t * const pPublishInfo,
                                               uint8_t * pBuffer,
                                               size_t * pSize,
                                               uint16_t packetIdentifier );
static IotSerializerError_t _serializePubAck( uint16_t packetIdentifier,
                                              uint8_t * pBuffer,
                                              size_t * pSize );



static IotSerializerError_t _serializeSubscribe( const IotMqttSubscription_t * const pSubscriptionList,
                                                 size_t subscriptionCount,
                                                 uint8_t * const pBuffer,
                                                 size_t * const pSize,
                                                 uint16_t packetIdentifier );

static IotSerializerError_t _serializeUnSubscribe( const IotMqttSubscription_t * const pSubscriptionList,
                                                   size_t subscriptionCount,
                                                   uint8_t * const pBuffer,
                                                   size_t * const pSize,
                                                   uint16_t packetIdentifier );

static IotSerializerError_t _serializeDisconnect( uint8_t * const pBuffer,
                                                  size_t * const pSize );


static IotSerializerError_t _serializePingRequest( uint8_t * const pBuffer,
                                                   size_t * const pSize );

#if LIBRARY_LOG_LEVEL > AWS_IOT_LOG_NONE

/**
 * @brief If logging is enabled, define a log configuration that only prints the log
 * string. This is used when printing out details of deserialized MQTT packets.
 */
    static const IotLogConfig_t _logHideAll =
    {
        .hideLibraryName = true,
        .hideLogLevel    = true,
        .hideTimestring  = true
    };
#endif


static IotMutex_t _packetIdentifierMutex;


/* Declaration of snprintf. The header stdio.h is not included because it
 * includes conflicting symbols on some platforms. */
extern int snprintf( char * s,
                     size_t n,
                     const char * format,
                     ... );

/*-----------------------------------------------------------*/

static uint16_t _nextPacketIdentifier( void )
{
    static uint16_t nextPacketIdentifier = 1;
    uint16_t newPacketIdentifier = 0;

    /* Lock the packet identifier mutex so that only one thread may read and
     * modify nextPacketIdentifier. */
    IotMutex_Lock( &_packetIdentifierMutex );

    /* Read the next packet identifier. */
    newPacketIdentifier = nextPacketIdentifier;

    /* The next packet identifier will be greater by 2. This prevents packet
     * identifiers from ever being 0, which is not allowed by MQTT 3.1.1. Packet
     * identifiers will follow the sequence 1,3,5...65535,1,3,5... */
    nextPacketIdentifier = ( uint16_t ) ( nextPacketIdentifier + ( ( uint16_t ) 2 ) );

    /* Unlock the packet identifier mutex. */
    IotMutex_Unlock( &_packetIdentifierMutex );

    return newPacketIdentifier;
}

static IotSerializerError_t _serializeConnect( const IotMqttConnectInfo_t * const pConnectInfo,
                                               uint8_t * const pBuffer,
                                               size_t * const pSize )
{
    IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;
    IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;
    IotSerializerEncoderObject_t connectMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;
    IotSerializerScalarData_t data = { 0 };

    error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );

    if( error == IOT_SERIALIZER_SUCCESS )
    {
        error = IOT_BLE_MESG_ENCODER.openContainer(
            &encoderObj,
            &connectMap,
            _NUM_CONNECT_PARMAS );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_CONNECT;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_MSG_TYPE, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;
        data.value.u.string.pString = ( uint8_t * ) pConnectInfo->pClientIdentifier;
        data.value.u.string.length = pConnectInfo->clientIdentifierLength;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_CLIENT_ID, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;
        data.value.u.string.pString = ( uint8_t * ) clientcredentialMQTT_BROKER_ENDPOINT;
        data.value.u.string.length = strlen( clientcredentialMQTT_BROKER_ENDPOINT );
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_BROKER_EP, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_BOOL;
        data.value.u.booleanValue = pConnectInfo->cleanSession;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_CLEAN_SESSION, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &connectMap );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pBuffer == NULL )
        {
            *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );
        }
        else
        {
            *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );
        }

        IOT_BLE_MESG_ENCODER.destroy( &encoderObj );
        error = IOT_SERIALIZER_SUCCESS;
    }

    return error;
}

static IotSerializerError_t _serializePublish( const IotMqttPublishInfo_t * const pPublishInfo,
                                               uint8_t * pBuffer,
                                               size_t * pSize,
                                               uint16_t packetIdentifier )
{
    IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;
    IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;
    IotSerializerEncoderObject_t publishMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;
    IotSerializerScalarData_t data = { 0 };
    uint16_t numPublishParams = _getNumPublishParams( pPublishInfo );

    error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );

    if( error == IOT_SERIALIZER_SUCCESS )
    {
        error = IOT_BLE_MESG_ENCODER.openContainer(
            &encoderObj,
            &publishMap,
            numPublishParams );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_PUBLISH;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_MSG_TYPE, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;
        data.value.u.string.pString = ( uint8_t * ) pPublishInfo->pTopicName;
        data.value.u.string.length = pPublishInfo->topicNameLength;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_TOPIC, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = pPublishInfo->qos;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_QOS, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_BYTE_STRING;
        data.value.u.string.pString = ( uint8_t * ) pPublishInfo->pPayload;
        data.value.u.string.length = pPublishInfo->payloadLength;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_PAYLOAD, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pPublishInfo->qos != 0 )
        {
            data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
            data.value.u.signedInt = packetIdentifier;
            error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_MESSAGE_ID, data );
        }
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &publishMap );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pBuffer == NULL )
        {
            *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );
        }
        else
        {
            *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );
        }

        IOT_BLE_MESG_ENCODER.destroy( &encoderObj );
        error = IOT_SERIALIZER_SUCCESS;
    }

    return error;
}

static IotSerializerError_t _serializePubAck( uint16_t packetIdentifier,
                                              uint8_t * pBuffer,
                                              size_t * pSize )

{
    IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;
    IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;
    IotSerializerEncoderObject_t pubAckMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;
    IotSerializerScalarData_t data = { 0 };

    error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );

    if( error == IOT_SERIALIZER_SUCCESS )
    {
        error = IOT_BLE_MESG_ENCODER.openContainer(
            &encoderObj,
            &pubAckMap,
            _NUM_PUBACK_PARMAS );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_PUBACK;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &pubAckMap, IOT_BLE_MQTT_MSG_TYPE, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = packetIdentifier;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &pubAckMap, IOT_BLE_MQTT_MESSAGE_ID, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &pubAckMap );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pBuffer == NULL )
        {
            *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );
        }
        else
        {
            *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );
        }

        IOT_BLE_MESG_ENCODER.destroy( &encoderObj );
        error = IOT_SERIALIZER_SUCCESS;
    }

    return error;
}


static IotSerializerError_t _serializeSubscribe( const IotMqttSubscription_t * const pSubscriptionList,
                                                 size_t subscriptionCount,
                                                 uint8_t * const pBuffer,
                                                 size_t * const pSize,
                                                 uint16_t packetIdentifier )
{
    IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;
    IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;
    IotSerializerEncoderObject_t subscribeMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;
    IotSerializerEncoderObject_t subscriptionArray = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_ARRAY;
    IotSerializerScalarData_t data = { 0 };
    uint16_t idx;

    error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );

    if( error == IOT_SERIALIZER_SUCCESS )
    {
        error = IOT_BLE_MESG_ENCODER.openContainer(
            &encoderObj,
            &subscribeMap,
            _NUM_SUBACK_PARAMS );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_SUBSCRIBE;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MSG_TYPE, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.openContainerWithKey(
            &subscribeMap,
            IOT_BLE_MQTT_TOPIC_LIST,
            &subscriptionArray,
            subscriptionCount );
    }

    for( idx = 0; idx < subscriptionCount; idx++ )
    {
        if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
        {
            data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;
            data.value.u.string.pString = ( uint8_t * ) pSubscriptionList[ idx ].pTopicFilter;
            data.value.u.string.length = pSubscriptionList[ idx ].topicFilterLength;
            error = IOT_BLE_MESG_ENCODER.append( &subscriptionArray, data );
        }
        else
        {
            break;
        }
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &subscribeMap, &subscriptionArray );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.openContainerWithKey(
            &subscribeMap,
            IOT_BLE_MQTT_QOS_LIST,
            &subscriptionArray,
            subscriptionCount );
    }

    for( idx = 0; idx < subscriptionCount; idx++ )
    {
        if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
        {
            data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
            data.value.u.signedInt = pSubscriptionList[ idx ].qos;
            error = IOT_BLE_MESG_ENCODER.append( &subscriptionArray, data );
        }
        else
        {
            break;
        }
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &subscribeMap, &subscriptionArray );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = packetIdentifier;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MESSAGE_ID, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &subscribeMap );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pBuffer == NULL )
        {
            *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );
        }
        else
        {
            *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );
        }

        IOT_BLE_MESG_ENCODER.destroy( &encoderObj );
        error = IOT_SERIALIZER_SUCCESS;
    }

    return error;
}

static IotSerializerError_t _serializeUnSubscribe( const IotMqttSubscription_t * const pSubscriptionList,
                                                   size_t subscriptionCount,
                                                   uint8_t * const pBuffer,
                                                   size_t * const pSize,
                                                   uint16_t packetIdentifier )
{
    IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;
    IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;
    IotSerializerEncoderObject_t subscribeMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;
    IotSerializerEncoderObject_t subscriptionArray = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_ARRAY;
    IotSerializerScalarData_t data = { 0 };
    uint16_t idx;

    error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );

    if( error == IOT_SERIALIZER_SUCCESS )
    {
        error = IOT_BLE_MESG_ENCODER.openContainer(
            &encoderObj,
            &subscribeMap,
            _NUM_UNSUBACK_PARAMS );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_UNSUBSCRIBE;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MSG_TYPE, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.openContainerWithKey(
            &subscribeMap,
            IOT_BLE_MQTT_TOPIC_LIST,
            &subscriptionArray,
            subscriptionCount );
    }

    for( idx = 0; idx < subscriptionCount; idx++ )
    {
        if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
        {
            data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;
            data.value.u.string.pString = ( uint8_t * ) pSubscriptionList[ idx ].pTopicFilter;
            data.value.u.string.length = pSubscriptionList[ idx ].topicFilterLength;
            error = IOT_BLE_MESG_ENCODER.append( &subscriptionArray, data );
        }
        else
        {
            break;
        }
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &subscribeMap, &subscriptionArray );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = packetIdentifier;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MESSAGE_ID, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &subscribeMap );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pBuffer == NULL )
        {
            *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );
        }
        else
        {
            *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );
        }

        IOT_BLE_MESG_ENCODER.destroy( &encoderObj );
        error = IOT_SERIALIZER_SUCCESS;
    }

    return error;
}

static IotSerializerError_t _serializeDisconnect( uint8_t * const pBuffer,
                                                  size_t * const pSize )
{
    IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;
    IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;
    IotSerializerEncoderObject_t disconnectMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;
    IotSerializerScalarData_t data = { 0 };

    error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );

    if( error == IOT_SERIALIZER_SUCCESS )
    {
        error = IOT_BLE_MESG_ENCODER.openContainer(
            &encoderObj,
            &disconnectMap,
            _NUM_DISCONNECT_PARAMS );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_DISCONNECT;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &disconnectMap, IOT_BLE_MQTT_MSG_TYPE, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &disconnectMap );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pBuffer == NULL )
        {
            *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );
        }
        else
        {
            *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );
        }

        IOT_BLE_MESG_ENCODER.destroy( &encoderObj );
        error = IOT_SERIALIZER_SUCCESS;
    }

    return error;
}

static IotSerializerError_t _serializePingRequest( uint8_t * const pBuffer,
                                                   size_t * const pSize )
{
    IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;
    IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;
    IotSerializerEncoderObject_t pingRequest = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;
    IotSerializerScalarData_t data = { 0 };

    error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );

    if( error == IOT_SERIALIZER_SUCCESS )
    {
        error = IOT_BLE_MESG_ENCODER.openContainer(
            &encoderObj,
            &pingRequest,
            _NUM_PINGREQUEST_PARAMS );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;
        data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_PINGREQ;
        error = IOT_BLE_MESG_ENCODER.appendKeyValue( &pingRequest, IOT_BLE_MQTT_MSG_TYPE, data );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &pingRequest );
    }

    if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )
    {
        if( pBuffer == NULL )
        {
            *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );
        }
        else
        {
            *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );
        }

        IOT_BLE_MESG_ENCODER.destroy( &encoderObj );
        error = IOT_SERIALIZER_SUCCESS;
    }

    return error;
}


bool IotBleMqtt_InitSerialize( void )
{
    /* Create the packet identifier mutex. */
    return IotMutex_Create( &_packetIdentifierMutex, false );
}

void IotBleMqtt_CleanupSerialize( void )
{
    /* Destroy the packet identifier mutex */
    IotMutex_Destroy( &_packetIdentifierMutex );
}


IotMqttError_t IotBleMqtt_SerializeConnect( const IotMqttConnectInfo_t * const pConnectInfo,
                                            uint8_t ** const pConnectPacket,
                                            size_t * const pPacketSize )
{
    uint8_t * pBuffer = NULL;
    size_t bufLen = 0;
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;


    error = _serializeConnect( pConnectInfo, NULL, &bufLen );

    if( error != IOT_SERIALIZER_SUCCESS )
    {
        IotLogError( "Failed to find length of serialized CONNECT message, error = %d", error );
        ret = IOT_MQTT_BAD_PARAMETER;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pBuffer = IotMqtt_MallocMessage( bufLen );

        /* If Memory cannot be allocated log an error and return */
        if( pBuffer == NULL )
        {
            IotLogError( "Failed to allocate memory for CONNECT packet." );
            ret = IOT_MQTT_NO_MEMORY;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = _serializeConnect( pConnectInfo, pBuffer, &bufLen );

        if( error != IOT_SERIALIZER_SUCCESS )
        {
            IotLogError( "Failed to serialize CONNECT message, error = %d", error );
            ret = IOT_MQTT_BAD_PARAMETER;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        *pConnectPacket = pBuffer;
        *pPacketSize = bufLen;
    }
    else
    {
        *pConnectPacket = NULL;
        *pPacketSize = 0;

        if( pBuffer != NULL )
        {
            IotMqtt_FreeMessage( pBuffer );
        }
    }

    return ret;
}

IotMqttError_t IotBleMqtt_DeserializeConnack( _mqttPacket_t * pConnack )
{
    IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;
    int64_t respCode = 0L;

    error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pConnack->pRemainingData, pConnack->remainingLength );

    if( ( error != IOT_SERIALIZER_SUCCESS ) ||
        ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )
    {
        IotLogError( "Malformed CONNACK, decoding the packet failed, decoder error = %d, type: %d", error, decoderObj.type );
        ret = IOT_MQTT_BAD_RESPONSE;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_STATUS, &decoderValue );

        if( ( error != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
        {
            IotLogError( "Invalid CONNACK, response code decode failed, error = %d, decoded value type = %d", error, decoderValue.type );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            respCode = decoderValue.u.value.u.signedInt;

            if( ( respCode != IOT_BLE_MQTT_STATUS_CONNECTING ) &&
                ( respCode != IOT_BLE_MQTT_STATUS_CONNECTED ) )
            {
                ret = IOT_MQTT_SERVER_REFUSED;
            }
        }
    }

    IOT_BLE_MESG_DECODER.destroy( &decoderObj );

    return ret;
}

IotMqttError_t IotBleMqtt_SerializePublish( const IotMqttPublishInfo_t * const pPublishInfo,
                                            uint8_t ** const pPublishPacket,
                                            size_t * const pPacketSize,
                                            uint16_t * const pPacketIdentifier,
                                            uint8_t ** pPacketIdentifierHigh )
{
    uint8_t * pBuffer = NULL;
    size_t bufLen = 0;
    uint16_t usPacketIdentifier = 0;
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    ( void ) pPacketIdentifierHigh;

    if( pPublishInfo->qos != 0 )
    {
        usPacketIdentifier = _nextPacketIdentifier();
    }

    error = _serializePublish( pPublishInfo, NULL, &bufLen, usPacketIdentifier );

    if( error != IOT_SERIALIZER_SUCCESS )
    {
        IotLogError( "Failed to find size of serialized PUBLISH message, error = %d", error );
        ret = IOT_MQTT_BAD_PARAMETER;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pBuffer = IotMqtt_MallocMessage( bufLen );

        /* If Memory cannot be allocated log an error and return */
        if( pBuffer == NULL )
        {
            IotLogError( "Failed to allocate memory for PUBLISH packet." );
            ret = IOT_MQTT_NO_MEMORY;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = _serializePublish( pPublishInfo, pBuffer, &bufLen, usPacketIdentifier );

        if( error != IOT_SERIALIZER_SUCCESS )
        {
            IotLogError( "Failed to serialize PUBLISH message, error = %d", error );
            ret = IOT_MQTT_BAD_PARAMETER;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        *pPublishPacket = pBuffer;
        *pPacketSize = bufLen;
        *pPacketIdentifier = usPacketIdentifier;
    }
    else
    {
        if( pBuffer != NULL )
        {
            IotMqtt_FreeMessage( pBuffer );
        }

        *pPublishPacket = NULL;
        *pPacketSize = 0;
    }

    return ret;
}

void IotBleMqtt_PublishSetDup( uint8_t * const pPublishPacket,
                               uint8_t * pPacketIdentifierHigh,
                               uint16_t * const pNewPacketIdentifier )
{
    /** TODO: Currently DUP flag is not supported by BLE SDKs **/
}

IotMqttError_t IotBleMqtt_DeserializePublish( _mqttPacket_t * pPublish )
{
    IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };
    IotSerializerError_t xSerializerRet;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    xSerializerRet = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pPublish->pRemainingData, pPublish->remainingLength );

    if( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||
        ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )
    {
        IotLogError( "Decoding PUBLISH packet failed, decoder error = %d, object type = %d", xSerializerRet, decoderObj.type );
        ret = IOT_MQTT_BAD_RESPONSE;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_QOS, &decoderValue );

        if( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
        {
            IotLogError( "QOS Value decode failed, error = %d, decoded value type = %d", xSerializerRet, decoderValue.type );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            pPublish->u.pIncomingPublish->u.publish.publishInfo.qos = decoderValue.u.value.u.signedInt;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        decoderValue.u.value.u.string.pString = NULL;
        decoderValue.u.value.u.string.length = 0;
        xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_TOPIC, &decoderValue );

        if( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_TEXT_STRING ) )
        {
            IotLogError( "Topic value decode failed, error = %d", xSerializerRet );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            pPublish->u.pIncomingPublish->u.publish.publishInfo.pTopicName = ( const char * ) decoderValue.u.value.u.string.pString;
            pPublish->u.pIncomingPublish->u.publish.publishInfo.topicNameLength = decoderValue.u.value.u.string.length;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        decoderValue.u.value.u.string.pString = NULL;
        decoderValue.u.value.u.string.length = 0;
        xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_PAYLOAD, &decoderValue );

        if( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_BYTE_STRING ) )
        {
            IotLogError( "Payload value decode failed, error = %d", xSerializerRet );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            pPublish->u.pIncomingPublish->u.publish.publishInfo.pPayload = ( const char * ) decoderValue.u.value.u.string.pString;
            pPublish->u.pIncomingPublish->u.publish.publishInfo.payloadLength = decoderValue.u.value.u.string.length;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        if( pPublish->u.pIncomingPublish->u.publish.publishInfo.qos != 0 )
        {
            xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );

            if( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||
                ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
            {
                IotLogError( "Message identifier decode failed, error = %d, decoded value type = %d", xSerializerRet, decoderValue.type );
                ret = IOT_MQTT_BAD_RESPONSE;
            }
            else
            {
                pPublish->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;
            }
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pPublish->u.pIncomingPublish->u.publish.publishInfo.retain = false;
    }

    IOT_BLE_MESG_DECODER.destroy( &decoderObj );

    return ret;
}

IotMqttError_t IotBleMqtt_SerializePuback( uint16_t packetIdentifier,
                                           uint8_t ** const pPubackPacket,
                                           size_t * const pPacketSize )
{
    uint8_t * pBuffer = NULL;
    size_t bufLen = 0;
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    error = _serializePubAck( packetIdentifier, NULL, &bufLen );

    if( error != IOT_SERIALIZER_SUCCESS )
    {
        IotLogError( "Failed to find size of serialized PUBACK message, error = %d", error );
        ret = IOT_MQTT_BAD_PARAMETER;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pBuffer = IotMqtt_MallocMessage( bufLen );

        /* If Memory cannot be allocated log an error and return */
        if( pBuffer == NULL )
        {
            IotLogError( "Failed to allocate memory for PUBACK packet, packet identifier = %d", packetIdentifier );
            ret = IOT_MQTT_NO_MEMORY;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = _serializePubAck( packetIdentifier, pBuffer, &bufLen );

        if( error != IOT_SERIALIZER_SUCCESS )
        {
            IotLogError( "Failed to find size of serialized PUBACK message, error = %d", error );
            ret = IOT_MQTT_BAD_PARAMETER;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        *pPubackPacket = pBuffer;
        *pPacketSize = bufLen;
    }
    else
    {
        if( pBuffer != NULL )
        {
            IotMqtt_FreeMessage( pBuffer );
        }

        *pPubackPacket = NULL;
        *pPacketSize = 0;
    }

    return ret;
}

IotMqttError_t IotBleMqtt_DeserializePuback( _mqttPacket_t * pPuback )
{
    IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pPuback->pRemainingData, pPuback->remainingLength );

    if( ( error != IOT_SERIALIZER_SUCCESS ) ||
        ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )
    {
        IotLogError( "Malformed PUBACK, decoding the packet failed, decoder error = %d, object type: %d", error, decoderObj.type );
        ret = IOT_MQTT_BAD_RESPONSE;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );

        if( ( error != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
        {
            IotLogError( "Message ID decode failed, error = %d, decoded value type = %d", error, decoderValue.type );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            pPuback->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;
        }
    }

    IOT_BLE_MESG_DECODER.destroy( &decoderObj );

    return ret;
}

IotMqttError_t IotBleMqtt_SerializeSubscribe( const IotMqttSubscription_t * const pSubscriptionList,
                                              size_t subscriptionCount,
                                              uint8_t ** const pSubscribePacket,
                                              size_t * const pPacketSize,
                                              uint16_t * const pPacketIdentifier )
{
    uint8_t * pBuffer = NULL;
    size_t bufLen = 0;
    uint16_t usPacketIdentifier = 0;
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    usPacketIdentifier = _nextPacketIdentifier();

    error = _serializeSubscribe( pSubscriptionList, subscriptionCount, NULL, &bufLen, usPacketIdentifier );

    if( error != IOT_SERIALIZER_SUCCESS )
    {
        IotLogError( "Failed to find serialized length of SUBSCRIBE message, error = %d", error );
        ret = IOT_MQTT_BAD_PARAMETER;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pBuffer = IotMqtt_MallocMessage( bufLen );

        /* If Memory cannot be allocated log an error and return */
        if( pBuffer == NULL )
        {
            IotLogError( "Failed to allocate memory for SUBSCRIBE message." );
            ret = IOT_MQTT_NO_MEMORY;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = _serializeSubscribe( pSubscriptionList, subscriptionCount, pBuffer, &bufLen, usPacketIdentifier );

        if( error != IOT_SERIALIZER_SUCCESS )
        {
            IotLogError( "Failed to serialize SUBSCRIBE message, error = %d", error );
            ret = IOT_MQTT_BAD_PARAMETER;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        *pSubscribePacket = pBuffer;
        *pPacketSize = bufLen;
        *pPacketIdentifier = usPacketIdentifier;
    }
    else
    {
        if( pBuffer != NULL )
        {
            IotMqtt_FreeMessage( pBuffer );
        }

        *pSubscribePacket = NULL;
        *pPacketSize = 0;
    }

    return ret;
}

IotMqttError_t IotBleMqtt_DeserializeSuback( _mqttPacket_t * pSuback )
{
    IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };
    IotSerializerError_t error;
    int64_t subscriptionStatus;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pSuback->pRemainingData, pSuback->remainingLength );

    if( ( error != IOT_SERIALIZER_SUCCESS ) ||
        ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )
    {
        IotLogError( "Malformed SUBACK, decoding the packet failed, decoder error = %d, type: %d", error, decoderObj.type );
        ret = IOT_MQTT_BAD_RESPONSE;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );

        if( ( error != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
        {
            IotLogError( "Message ID decode failed, error = %d, decoded value type = %d", error, decoderValue.type );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            pSuback->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_STATUS, &decoderValue );

        if( ( error != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
        {
            IotLogError( "Status code decode failed, error = %d, decoded value type = %d", error, decoderValue.type );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            subscriptionStatus = ( uint16_t ) decoderValue.u.value.u.signedInt;

            switch( subscriptionStatus )
            {
                case 0x00:
                case 0x01:
                case 0x02:
                    IotLog( IOT_LOG_DEBUG,
                            &_logHideAll,
                            "Topic accepted, max QoS %hhu.", subscriptionStatus );
                    ret = IOT_MQTT_SUCCESS;
                    break;

                case 0x80:
                    IotLog( IOT_LOG_DEBUG,
                            &_logHideAll,
                            "Topic refused." );

                    /* Remove a rejected subscription from the subscription manager. */
                    _IotMqtt_RemoveSubscriptionByPacket(
                        pSuback->u.pMqttConnection,
                        pSuback->packetIdentifier,
                        0 );
                    ret = IOT_MQTT_SERVER_REFUSED;
                    break;

                default:
                    IotLog( IOT_LOG_DEBUG,
                            &_logHideAll,
                            "Bad SUBSCRIBE status %hhu.", subscriptionStatus );

                    ret = IOT_MQTT_BAD_RESPONSE;
                    break;
            }
        }
    }

    IOT_BLE_MESG_DECODER.destroy( &decoderObj );

    return ret;
}

IotMqttError_t IotBleMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * const pSubscriptionList,
                                                size_t subscriptionCount,
                                                uint8_t ** const pUnsubscribePacket,
                                                size_t * const pPacketSize,
                                                uint16_t * const pPacketIdentifier )
{
    uint8_t * pBuffer = NULL;
    size_t bufLen = 0;
    uint16_t usPacketIdentifier = 0;
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    usPacketIdentifier = _nextPacketIdentifier();

    error = _serializeUnSubscribe( pSubscriptionList, subscriptionCount, NULL, &bufLen, usPacketIdentifier );

    if( error != IOT_SERIALIZER_SUCCESS )
    {
        IotLogError( "Failed to find serialized length of UNSUBSCRIBE message, error = %d", error );
        ret = IOT_MQTT_BAD_PARAMETER;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pBuffer = IotMqtt_MallocMessage( bufLen );

        /* If Memory cannot be allocated log an error and return */
        if( pBuffer == NULL )
        {
            IotLogError( "Failed to allocate memory for UNSUBSCRIBE message." );
            ret = IOT_MQTT_NO_MEMORY;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = _serializeUnSubscribe( pSubscriptionList, subscriptionCount, pBuffer, &bufLen, usPacketIdentifier );

        if( error != IOT_SERIALIZER_SUCCESS )
        {
            IotLogError( "Failed to serialize UNSUBSCRIBE message, error = %d", error );
            ret = IOT_MQTT_BAD_PARAMETER;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        *pUnsubscribePacket = pBuffer;
        *pPacketSize = bufLen;
        *pPacketIdentifier = usPacketIdentifier;
    }
    else
    {
        if( pBuffer != NULL )
        {
            IotMqtt_FreeMessage( pBuffer );
        }

        *pUnsubscribePacket = NULL;
        *pPacketSize = 0;
    }

    return ret;
}

IotMqttError_t IotBleMqtt_DeserializeUnsuback( _mqttPacket_t * pUnsuback )
{
    IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pUnsuback->pRemainingData, pUnsuback->remainingLength );

    if( ( error != IOT_SERIALIZER_SUCCESS ) ||
        ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )
    {
        IotLogError( "Malformed UNSUBACK, decoding the packet failed, decoder error = %d, type:%d ", error, decoderObj.type );
        ret = IOT_MQTT_BAD_RESPONSE;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );

        if( ( error != IOT_SERIALIZER_SUCCESS ) ||
            ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
        {
            IotLogError( "UNSUBACK Message identifier decode failed, error = %d, decoded value type = %d", error, decoderValue.type );
            ret = IOT_MQTT_BAD_RESPONSE;
        }
        else
        {
            pUnsuback->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;
        }
    }

    IOT_BLE_MESG_DECODER.destroy( &decoderObj );

    return IOT_MQTT_SUCCESS;
}

IotMqttError_t IotBleMqtt_SerializeDisconnect( uint8_t ** const pDisconnectPacket,
                                               size_t * const pPacketSize )
{
    uint8_t * pBuffer = NULL;
    size_t bufLen = 0;
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    error = _serializeDisconnect( NULL, &bufLen );

    if( error != IOT_SERIALIZER_SUCCESS )
    {
        IotLogError( "Failed to find serialized length of DISCONNECT message, error = %d", error );
        ret = IOT_MQTT_BAD_PARAMETER;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pBuffer = IotMqtt_MallocMessage( bufLen );

        /* If Memory cannot be allocated log an error and return */
        if( pBuffer == NULL )
        {
            IotLogError( "Failed to allocate memory for DISCONNECT message." );
            ret = IOT_MQTT_NO_MEMORY;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = _serializeDisconnect( pBuffer, &bufLen );

        if( error != IOT_SERIALIZER_SUCCESS )
        {
            IotLogError( "Failed to serialize DISCONNECT message, error = %d", error );
            ret = IOT_MQTT_BAD_PARAMETER;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        *pDisconnectPacket = pBuffer;
        *pPacketSize = bufLen;
    }
    else
    {
        if( pBuffer != NULL )
        {
            IotMqtt_FreeMessage( pBuffer );
        }

        *pDisconnectPacket = NULL;
        *pPacketSize = 0;
    }

    return ret;
}

size_t IotBleMqtt_GetRemainingLength( void * pNetworkConnection,
                                      const IotNetworkInterface_t * pNetworkInterface )
{
    const uint8_t * pBuffer;
    size_t length;

    IotBleDataTransfer_PeekReceiveBuffer( *( IotBleDataTransferChannel_t ** ) ( pNetworkConnection ), &pBuffer, &length );

    return length;
}


uint8_t IotBleMqtt_GetPacketType( void * pNetworkConnection,
                                  const IotNetworkInterface_t * pNetworkInterface )
{
    IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };
    IotSerializerError_t error;
    uint8_t value = 0xFF, packetType = _INVALID_MQTT_PACKET_TYPE;
    const uint8_t * pBuffer;
    size_t length;

    IotBleDataTransfer_PeekReceiveBuffer( *( IotBleDataTransferChannel_t ** ) ( pNetworkConnection ), &pBuffer, &length );

    error = IOT_BLE_MESG_DECODER.init( &decoderObj, pBuffer, length );

    if( ( error == IOT_SERIALIZER_SUCCESS ) &&
        ( decoderObj.type == IOT_SERIALIZER_CONTAINER_MAP ) )
    {
        error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MSG_TYPE, &decoderValue );

        if( ( error == IOT_SERIALIZER_SUCCESS ) &&
            ( decoderValue.type == IOT_SERIALIZER_SCALAR_SIGNED_INT ) )
        {
            value = ( uint16_t ) decoderValue.u.value.u.signedInt;

            /** Left shift by 4 bits as MQTT library expects packet type to be upper 4 bits **/
            packetType = value << 4;
        }
        else
        {
            IotLogError( "Packet type decode failed, error = %d, decoded value type = %d", error, decoderValue.type );
        }
    }
    else
    {
        IotLogError( "Decoding the packet failed, decoder error = %d, type = %d", error, decoderObj.type );
    }

    IOT_BLE_MESG_DECODER.destroy( &decoderObj );

    return packetType;
}

IotMqttError_t IotBleMqtt_SerializePingreq( uint8_t ** const pPingreqPacket,
                                            size_t * const pPacketSize )
{
    uint8_t * pBuffer = NULL;
    size_t bufLen = 0;
    IotSerializerError_t error;
    IotMqttError_t ret = IOT_MQTT_SUCCESS;

    error = _serializePingRequest( NULL, &bufLen );

    if( error != IOT_SERIALIZER_SUCCESS )
    {
        IotLogError( "Failed to find serialized length of DISCONNECT message, error = %d", error );
        ret = IOT_MQTT_BAD_PARAMETER;
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        pBuffer = IotMqtt_MallocMessage( bufLen );

        /* If Memory cannot be allocated log an error and return */
        if( pBuffer == NULL )
        {
            IotLogError( "Failed to allocate memory for DISCONNECT message." );
            ret = IOT_MQTT_NO_MEMORY;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        error = _serializePingRequest( pBuffer, &bufLen );

        if( error != IOT_SERIALIZER_SUCCESS )
        {
            IotLogError( "Failed to serialize DISCONNECT message, error = %d", error );
            ret = IOT_MQTT_BAD_PARAMETER;
        }
    }

    if( ret == IOT_MQTT_SUCCESS )
    {
        *pPingreqPacket = pBuffer;
        *pPacketSize = bufLen;
    }
    else
    {
        if( pBuffer != NULL )
        {
            IotMqtt_FreeMessage( pBuffer );
        }

        *pPingreqPacket = NULL;
        *pPacketSize = 0;
    }

    return ret;
}

IotMqttError_t IotBleMqtt_DeserializePingresp( _mqttPacket_t * pPingresp )
{
    /* Ping Response for BLE contains only packet type field in CBOR, which is already decoded
     * in IotBleMqtt_GetPacketType() function. Returning IOT_MQTT_SUCCESS. */
    return IOT_MQTT_SUCCESS;
}

void IotBleMqtt_FreePacket( uint8_t * pPacket )
{
    IotMqtt_FreeMessage( pPacket );
}
