/*
 * Copyright 2017 Cisco Systems, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#include "vnf.h"
#include "nfapi_nr_interface.h"
#include "nfapi_nr_interface_scf.h"
#ifdef ENABLE_AERIAL
#include "nfapi/oai_integration/aerial/fapi_nvIPC.h"
#include "nfapi/oai_integration/aerial/fapi_vnf_p5.h"
#endif
#include "nfapi/oai_integration/vendor_ext.h"

void* vnf_malloc(nfapi_vnf_config_t* config, size_t size)
{
	if(config->malloc)
	{
		return (config->malloc)(size);
	}
	else
	{
		return calloc(1, size); 
	}
}
void vnf_free(nfapi_vnf_config_t* config, void* ptr)
{
	if(config->free)
	{
		return (config->free)(ptr);
	}
	else
	{
		return free(ptr); 
	}
}

void nfapi_vnf_phy_info_list_add(nfapi_vnf_config_t* config, nfapi_vnf_phy_info_t* info)
{
	NFAPI_TRACE(NFAPI_TRACE_INFO, "Adding phy p5_idx:%d phy_id:%d\n", info->p5_idx, info->phy_id);
	info->next = config->phy_list;
	config->phy_list = info;
}

nfapi_vnf_phy_info_t* nfapi_vnf_phy_info_list_find(nfapi_vnf_config_t* config, uint16_t phy_id)
{
	nfapi_vnf_phy_info_t* curr = config->phy_list;
	while(curr != 0)
	{
		if(curr->phy_id == phy_id)
			return curr;

		curr = curr->next;
	}

	return 0;
}




void nfapi_vnf_pnf_list_add(nfapi_vnf_config_t* config, nfapi_vnf_pnf_info_t* node)
{
	node->next = config->pnf_list;
	config->pnf_list = node;
}


nfapi_vnf_pnf_info_t* nfapi_vnf_pnf_list_find(nfapi_vnf_config_t* config, int p5_idx)
{
	NFAPI_TRACE(NFAPI_TRACE_DEBUG, "config->pnf_list:%p\n", config->pnf_list);

	nfapi_vnf_pnf_info_t* curr = config->pnf_list;
	while(curr != 0)
	{
		if(curr->p5_idx == p5_idx)
                {
                  NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s : curr->p5_idx:%d p5_idx:%d\n", __FUNCTION__, curr->p5_idx, p5_idx);
			return curr;
                        }

                NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s : curr->next:%p\n", __FUNCTION__, curr->next);

		curr = curr->next;
	}

	return 0;
}

void vnf_nr_handle_pnf_param_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s : NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PNF_PARAM.reponse\n");

		nfapi_nr_pnf_param_response_t msg;
			
		// unpack the message
		if (nfapi_nr_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			// Invoke the call back
			if(config->pnf_nr_param_resp)
			{
				(config->pnf_nr_param_resp)(config, p5_idx, &msg);
			}
		}	
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_pnf_param_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s : NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PNF_PARAM.reponse\n");

		nfapi_pnf_param_response_t msg;
			
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			// Invoke the call back
			if(config->pnf_param_resp)
			{
				(config->pnf_param_resp)(config, p5_idx, &msg);
			}
		}	
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}


void vnf_nr_handle_pnf_config_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PNF_CONFIG_RESPONSE\n");
		
		nfapi_nr_pnf_config_response_t msg;
		
		// unpack the message
		if (nfapi_nr_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			// Invoke the call back
			if(config->pnf_nr_config_resp)
			{
				(config->pnf_nr_config_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_pnf_config_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PNF_CONFIG_RESPONSE\n");
		
		nfapi_pnf_config_response_t msg;
		
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			// Invoke the call back
			if(config->pnf_config_resp)
			{
				(config->pnf_config_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_nr_handle_pnf_start_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PNF_START_RESPONSE\n");
	
		nfapi_nr_pnf_start_response_t msg;
	
		// unpack the message
		if (nfapi_nr_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->pnf_nr_start_resp)
			{
				(config->pnf_nr_start_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_pnf_start_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PNF_START_RESPONSE\n");
	
		nfapi_pnf_start_response_t msg;
	
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->pnf_start_resp)
			{
				(config->pnf_start_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_pnf_stop_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PNF_STOP_RESPONSE\n");
	
		nfapi_pnf_stop_response_t msg;

		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->pnf_stop_resp)
			{
				(config->pnf_stop_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}

		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);

	}
}

void vnf_handle_param_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{

	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PARAM_RESPONSE\n");
		
		nfapi_param_response_t msg;
		
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			
			if (msg.error_code == NFAPI_MSG_OK)
			{
				nfapi_vnf_phy_info_t* phy_info = nfapi_vnf_phy_info_list_find(config, msg.header.phy_id);
		
				if(msg.nfapi_config.p7_pnf_address_ipv4.tl.tag)
				{
					struct sockaddr_in sockAddr;
		
					(void)memcpy(&sockAddr.sin_addr.s_addr, msg.nfapi_config.p7_pnf_address_ipv4.address, NFAPI_IPV4_ADDRESS_LENGTH);
					NFAPI_TRACE(NFAPI_TRACE_INFO, "PNF P7 IPv4 address: %s\n", inet_ntoa(sockAddr.sin_addr));
		
					// store address
					phy_info->p7_pnf_address.sin_addr = sockAddr.sin_addr;
				}
		
				if(msg.nfapi_config.p7_pnf_address_ipv6.tl.tag)
				{
					struct sockaddr_in6 sockAddr6;
					char addr6[64];
					(void)memcpy(&sockAddr6.sin6_addr, msg.nfapi_config.p7_pnf_address_ipv6.address, NFAPI_IPV6_ADDRESS_LENGTH);
					NFAPI_TRACE(NFAPI_TRACE_INFO, "PNF P7 IPv6 address: %s\n", inet_ntop(AF_INET6, &sockAddr6.sin6_addr, addr6, sizeof(addr6)));
				}
				
				if (msg.nfapi_config.p7_pnf_port.tl.tag)
				{
					NFAPI_TRACE(NFAPI_TRACE_INFO, "PNF P7 Port: %d\n", msg.nfapi_config.p7_pnf_port.value);
		
					// store port
					phy_info->p7_pnf_address.sin_port = htons(msg.nfapi_config.p7_pnf_port.value);
				}
			}
			
			if(config->param_resp)
			{
				(config->param_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_nr_handle_param_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{

	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received PARAM_RESPONSE\n");
		
		nfapi_nr_param_response_scf_t msg;
		
		// unpack the message
		if (nfapi_nr_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			
			if (msg.error_code == NFAPI_NR_PARAM_MSG_OK)
			{
				nfapi_vnf_phy_info_t* phy_info = nfapi_vnf_phy_info_list_find(config, msg.header.phy_id);
		
				if(msg.nfapi_config.p7_pnf_address_ipv4.tl.tag)
				{
					struct sockaddr_in sockAddr;
		
					(void)memcpy(&sockAddr.sin_addr.s_addr, msg.nfapi_config.p7_pnf_address_ipv4.address, NFAPI_IPV4_ADDRESS_LENGTH);
					NFAPI_TRACE(NFAPI_TRACE_INFO, "PNF P7 IPv4 address: %s\n", inet_ntoa(sockAddr.sin_addr));
		
					// store address
					phy_info->p7_pnf_address.sin_addr = sockAddr.sin_addr;
				}
		
				if(msg.nfapi_config.p7_pnf_address_ipv6.tl.tag)
				{
					struct sockaddr_in6 sockAddr6;
					char addr6[64];
					(void)memcpy(&sockAddr6.sin6_addr, msg.nfapi_config.p7_pnf_address_ipv6.address, NFAPI_IPV6_ADDRESS_LENGTH);
					NFAPI_TRACE(NFAPI_TRACE_INFO, "PNF P7 IPv6 address: %s\n", inet_ntop(AF_INET6, &sockAddr6.sin6_addr, addr6, sizeof(addr6)));
				}
				
				if (msg.nfapi_config.p7_pnf_port.tl.tag)
				{
					NFAPI_TRACE(NFAPI_TRACE_INFO, "PNF P7 Port: %d\n", msg.nfapi_config.p7_pnf_port.value);
		
					// store port
					phy_info->p7_pnf_address.sin_port = htons(msg.nfapi_config.p7_pnf_port.value);
				}
			}
			
			if(config->nr_param_resp)
			{
				(config->nr_param_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}


void vnf_nr_handle_config_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{

	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received CONFIG_RESPONSE\n");
			
		nfapi_nr_config_response_scf_t msg;
		
		// unpack the message
		if (nfapi_nr_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >=0 )
		{
			// check the error code:

			if (msg.error_code == NFAPI_NR_CONFIG_MSG_OK){
				if(config->nr_config_resp)
				{
					(config->nr_config_resp)(config, p5_idx, &msg);
				}
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
	
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_config_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{

	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received CONFIG_RESPONSE\n");
			
		nfapi_config_response_t msg;
		
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >=0 )
		{
			// check the error code:

			if (msg.error_code == NFAPI_MSG_OK){
				if(config->config_resp)
				{
					(config->config_resp)(config, p5_idx, &msg);
				}
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
	
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_start_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{	
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received START_RESPONSE\n");

		nfapi_start_response_t msg;
		
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->start_resp)
			{
				(config->start_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}

		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_nr_handle_start_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{	
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received START_RESPONSE\n");

		nfapi_nr_start_response_scf_t msg;
		
		// unpack the message
		if (nfapi_nr_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{	// check the error code
			if (msg.error_code == NFAPI_NR_START_MSG_OK){
				if(config->nr_start_resp)
				{
					(config->nr_start_resp)(config, p5_idx, &msg);
				}
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}

		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_stop_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{

	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received STOP.response\n");
	
		nfapi_stop_response_t msg;
			
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->stop_resp)
			{
				(config->stop_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
	
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_measurement_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received MEASUREMENT.response\n");
		
		nfapi_measurement_response_t msg;
		
		// unpack the message
		if (nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->measurement_resp)
			{
				(config->measurement_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_rssi_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received RSSI.response\n");		

		nfapi_rssi_response_t msg;
		
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->rssi_resp)
			{
				(config->rssi_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_rssi_indication(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received RSSI.indication\n");	

		nfapi_rssi_indication_t msg;
		
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->rssi_ind)
			{
				(config->rssi_ind)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_cell_search_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received CELL_SEARCH.response\n");
		
		nfapi_cell_search_response_t msg;
	
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->cell_search_resp)
			{
				(config->cell_search_resp)(config, p5_idx, &msg);
			}	
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_cell_search_indication(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "vnf_handle_cell_search_indication: NULL parameters\n");
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received CELL_SEARCH.indication\n");
	
		nfapi_cell_search_indication_t msg;
		
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->cell_search_ind)
			{
				(config->cell_search_ind)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "vnf_handle_cell_search_response: Unpack message failed, ignoring\n");
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_broadcast_detect_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received BROADCAST_DETECT.response\n");

		nfapi_broadcast_detect_response_t msg;

		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->broadcast_detect_resp)
			{
				(config->broadcast_detect_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_broadcast_detect_indication(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received BROADCAST_DETECT.indication\n");

		nfapi_broadcast_detect_indication_t msg;

		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->broadcast_detect_ind)
			{
				(config->broadcast_detect_ind)(config, p5_idx, &msg);
			}	
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
			return;
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_system_information_schedule_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received SYSTEM_INFORMATION_SCHEDULE.response\n");
	
		nfapi_system_information_schedule_response_t msg;
		
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->system_information_schedule_resp)
			{
				(config->system_information_schedule_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
			
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_system_information_schedule_indication(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received SYSTEM_INFORMATION_SCHEDULE.indication\n");

		nfapi_system_information_schedule_indication_t msg;

		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->system_information_schedule_ind)
			{
				(config->system_information_schedule_ind)(config, p5_idx, &msg);
			}	
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
	
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_system_information_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received SYSTEM_INFORMATION.response\n");

		nfapi_system_information_response_t msg;
		
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) >= 0)
		{
			if(config->system_information_resp)
			{
				(config->system_information_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
	
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_system_information_indication(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received SYSTEM_INFORMATION.indication\n");
		
		nfapi_system_information_indication_t msg;
		
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config) < 0)
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
			return;
		}
	
		if(config->system_information_ind)
		{
			(config->system_information_ind)(config, p5_idx, &msg);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
}

void vnf_handle_nmm_stop_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx)
{
	// ensure it's valid
	if (pRecvMsg == NULL || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "Received NMM_STOP.response\n");	
		
		nfapi_nmm_stop_response_t msg;	
		
		// unpack the message
		if (nfapi_p4_message_unpack(pRecvMsg, recvMsgLen, &msg, sizeof(nfapi_nmm_stop_response_t), &config->codec_config) >= 0)
		{
			if(config->nmm_stop_resp)
			{
				(config->nmm_stop_resp)(config, p5_idx, &msg);
			}
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
		}
		
		// make sure to release any dyanmic part of the message
		if(msg.vendor_extension)
			config->codec_config.deallocate(msg.vendor_extension);
	}
	
}

void vnf_handle_vendor_extension(void* pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx, uint16_t message_id)
{
	NFAPI_TRACE(NFAPI_TRACE_INFO, "%s\n", __FUNCTION__);
	
	if(config->allocate_p4_p5_vendor_ext && config->deallocate_p4_p5_vendor_ext)
	{
		uint16_t msg_size;
		
		nfapi_p4_p5_message_header_t* msg = config->allocate_p4_p5_vendor_ext(message_id, &msg_size);

		if(msg)
		{
			if(nfapi_p5_message_unpack(pRecvMsg, recvMsgLen, msg, msg_size, &config->codec_config) >= 0)
			{
				if(config->vendor_ext)
					config->vendor_ext(config, p5_idx, msg);
			}
			else
			{
				NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__);
			}
			
			config->deallocate_p4_p5_vendor_ext(msg);
		}
		else
		{
			NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to allocate vendor extention structure\n");
		}
	}
}

void vnf_nr_handle_p4_p5_message(void *pRecvMsg, int recvMsgLen, int p5_idx, nfapi_vnf_config_t* config)
{
	nfapi_p4_p5_message_header_t messageHeader;

	// validate the input params
	if(pRecvMsg == NULL || recvMsgLen < NFAPI_HEADER_LENGTH || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "vnf_handle_p4_p5_message: invalid input params\n");
		return;
	}

	// unpack the message header
	if (nfapi_p5_message_header_unpack(pRecvMsg, recvMsgLen, &messageHeader, sizeof(nfapi_p4_p5_message_header_t), &config->codec_config) < 0)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "Unpack message header failed, ignoring\n");
		return;
	}

	switch (messageHeader.message_id)
	{
		case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_RESPONSE:
			vnf_nr_handle_pnf_param_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_RESPONSE:
			vnf_nr_handle_pnf_config_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_NR_PHY_MSG_TYPE_PNF_START_RESPONSE:
			vnf_nr_handle_pnf_start_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_PNF_STOP_RESPONSE:
			vnf_handle_pnf_stop_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE:
			vnf_nr_handle_param_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE:
			vnf_nr_handle_config_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE:
			vnf_nr_handle_start_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE:
			vnf_handle_stop_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		default:
			{
				if(messageHeader.message_id >= NFAPI_VENDOR_EXT_MSG_MIN &&
				   messageHeader.message_id <= NFAPI_VENDOR_EXT_MSG_MAX)
				{
					vnf_handle_vendor_extension(pRecvMsg, recvMsgLen, config, p5_idx, messageHeader.message_id);
				}
				else
				{
					NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s P5 Unknown message ID %d\n", __FUNCTION__, messageHeader.message_id);
				}
			}
			break;
	}
}

void vnf_handle_p4_p5_message(void *pRecvMsg, int recvMsgLen, int p5_idx, nfapi_vnf_config_t* config)
{
	nfapi_p4_p5_message_header_t messageHeader;

	// validate the input params
	if(pRecvMsg == NULL || recvMsgLen < NFAPI_HEADER_LENGTH || config == NULL)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "vnf_handle_p4_p5_message: invalid input params\n");
		return;
	}

	// unpack the message header
	if (nfapi_p5_message_header_unpack(pRecvMsg, recvMsgLen, &messageHeader, sizeof(nfapi_p4_p5_message_header_t), &config->codec_config) < 0)
	{
		NFAPI_TRACE(NFAPI_TRACE_ERROR, "Unpack message header failed, ignoring\n");
		return;
	}

	switch (messageHeader.message_id)
	{
		case NFAPI_PNF_PARAM_RESPONSE:
			vnf_handle_pnf_param_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_PNF_CONFIG_RESPONSE:
			vnf_handle_pnf_config_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_PNF_START_RESPONSE:
			vnf_handle_pnf_start_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_PNF_STOP_RESPONSE:
			vnf_handle_pnf_stop_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_PARAM_RESPONSE:
			vnf_handle_param_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_CONFIG_RESPONSE:
			vnf_handle_config_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_START_RESPONSE:
			vnf_handle_start_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_STOP_RESPONSE:
			vnf_handle_stop_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_MEASUREMENT_RESPONSE:
			vnf_handle_measurement_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_RSSI_RESPONSE:
			vnf_handle_rssi_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_RSSI_INDICATION:
			vnf_handle_rssi_indication(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_CELL_SEARCH_RESPONSE:
			vnf_handle_cell_search_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_CELL_SEARCH_INDICATION:
			vnf_handle_cell_search_indication(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_BROADCAST_DETECT_RESPONSE:
			vnf_handle_broadcast_detect_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_BROADCAST_DETECT_INDICATION:
			vnf_handle_broadcast_detect_indication(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_SYSTEM_INFORMATION_SCHEDULE_RESPONSE:
			vnf_handle_system_information_schedule_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_SYSTEM_INFORMATION_SCHEDULE_INDICATION:
			vnf_handle_system_information_schedule_indication(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_SYSTEM_INFORMATION_RESPONSE:
			vnf_handle_system_information_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_SYSTEM_INFORMATION_INDICATION:
			vnf_handle_system_information_indication(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		case NFAPI_NMM_STOP_RESPONSE:
			vnf_handle_nmm_stop_response(pRecvMsg, recvMsgLen, config, p5_idx);
			break;

		default:
			{
				if(messageHeader.message_id >= NFAPI_VENDOR_EXT_MSG_MIN &&
				   messageHeader.message_id <= NFAPI_VENDOR_EXT_MSG_MAX)
				{
					vnf_handle_vendor_extension(pRecvMsg, recvMsgLen, config, p5_idx, messageHeader.message_id);
				}
				else
				{
					NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s P5 Unknown message ID %d\n", __FUNCTION__, messageHeader.message_id);
				}
			}
			break;
	}
}

int vnf_nr_read_dispatch_message(nfapi_vnf_config_t* config, nfapi_vnf_pnf_info_t* pnf)
{
  if (1) {
      int socket_connected = 1;

      // 1. Peek the message header
      // 2. If the message is larger than the stack buffer then create a dynamic buffer
      // 3. Read the buffer
      // 4. Handle the p5 message

      uint32_t header_buffer_size = NFAPI_HEADER_LENGTH;
      uint8_t header_buffer[header_buffer_size];

      uint32_t stack_buffer_size = 32; // should it be the size of then sctp_notificatoin structure
      uint8_t stack_buffer[stack_buffer_size];

      uint8_t* dynamic_buffer = 0;

      uint8_t* read_buffer = &stack_buffer[0];
      uint32_t message_size = 0;

      struct sockaddr_in addr;
      socklen_t addr_len = sizeof(addr);

      struct sctp_sndrcvinfo sndrcvinfo;
      (void)memset(&sndrcvinfo, 0, sizeof(struct sctp_sndrcvinfo));

      {
        int flags = MSG_PEEK;
        message_size =
            sctp_recvmsg(pnf->p5_sock, header_buffer, header_buffer_size, (struct sockaddr*)&addr, &addr_len, &sndrcvinfo, &flags);

        if (message_size == -1) {
          NFAPI_TRACE(NFAPI_TRACE_INFO, "VNF Failed to peek sctp message size errno:%d\n", errno);
          return 0;
        }

        nfapi_p4_p5_message_header_t header;
        int unpack_result = nfapi_p5_message_header_unpack(header_buffer, header_buffer_size, &header, sizeof(header), 0);
        if (unpack_result < 0) {
          NFAPI_TRACE(NFAPI_TRACE_INFO, "VNF Failed to decode message header %d\n", unpack_result);
          return 0;
        }
        message_size = header.message_length + header_buffer_size;

        // now have the size of the mesage
      }

      if (message_size > stack_buffer_size) {
        dynamic_buffer = (uint8_t*)malloc(message_size);

        if (dynamic_buffer == NULL) {
          // todo : add error mesage
          NFAPI_TRACE(NFAPI_TRACE_INFO, "VNF Failed to allocate dynamic buffer for sctp_recvmsg size:%d\n", message_size);
          return -1;
        }

        read_buffer = dynamic_buffer;
      }

      {
        int flags = 0;
        (void)memset(&sndrcvinfo, 0, sizeof(struct sctp_sndrcvinfo));

        int recvmsg_result =
            sctp_recvmsg(pnf->p5_sock, read_buffer, message_size, (struct sockaddr*)&addr, &addr_len, &sndrcvinfo, &flags);
        if (recvmsg_result == -1) {
          NFAPI_TRACE(NFAPI_TRACE_INFO, "Failed to read sctp message size errno:%d\n", errno);
        } else {
          if (flags & MSG_NOTIFICATION) {
          NFAPI_TRACE(NFAPI_TRACE_INFO, "Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

          // todo - handle the events
          } else {
          /*
          NFAPI_TRACE(NFAPI_TRACE_INFO, "Received message fd:%d from %s:%u assoc:%d on stream %d, PPID %d, length %d, flags 0x%x\n",
              pnf->p5_sock,
              inet_ntoa(addr.sin_addr),
              ntohs(addr.sin_port),
              sndrcvinfo.sinfo_assoc_id,
              sndrcvinfo.sinfo_stream,
              ntohl(sndrcvinfo.sinfo_ppid),
              message_size,
              flags);
          */

          // handle now if complete message in one or more segments
          if ((flags & 0x80) == 0x80) {
            // printf("\nVNF RECEIVES:\n");
            // for(int i=0; i<message_size; i++){
            // 	printf("%d", read_buffer[i]);
            // }
            // printf("\n");

            vnf_nr_handle_p4_p5_message(read_buffer, message_size, pnf->p5_idx, config);
          } else {
            NFAPI_TRACE(NFAPI_TRACE_WARN, "sctp_recvmsg: unhandled mode with flags 0x%x\n", flags);

            // assume socket disconnected
            NFAPI_TRACE(NFAPI_TRACE_WARN, "Disconnected socket\n");
            socket_connected = 0;
          }
          }
        }
      }

      if (dynamic_buffer) {
        free(dynamic_buffer);
      }

      return socket_connected;
  }
}

int vnf_read_dispatch_message(nfapi_vnf_config_t* config, nfapi_vnf_pnf_info_t* pnf)
{
	if(1)
	{
		int socket_connected = 1;

		// 1. Peek the message header
		// 2. If the message is larger than the stack buffer then create a dynamic buffer
		// 3. Read the buffer
		// 4. Handle the p5 message
		
		uint32_t header_buffer_size = NFAPI_HEADER_LENGTH;
		uint8_t header_buffer[header_buffer_size];
		memset(header_buffer, 0, header_buffer_size);

		uint32_t stack_buffer_size = 32; //should it be the size of then sctp_notificatoin structure
		uint8_t stack_buffer[stack_buffer_size];

		uint8_t* dynamic_buffer = 0;

		uint8_t* read_buffer = &stack_buffer[0];
		uint32_t message_size = 0;

		struct sockaddr_in addr;
		memset(&addr, 0, sizeof(addr));
		socklen_t addr_len = sizeof(addr);

		struct sctp_sndrcvinfo sndrcvinfo;
		(void)memset(&sndrcvinfo, 0, sizeof(struct sctp_sndrcvinfo));

		{
			int flags = MSG_PEEK;
			message_size = sctp_recvmsg(pnf->p5_sock, header_buffer, header_buffer_size, (struct sockaddr*)&addr, &addr_len, &sndrcvinfo,  &flags);

			if(message_size == -1)
			{
				NFAPI_TRACE(NFAPI_TRACE_INFO, "VNF Failed to peek sctp message size errno:%d\n", errno);
				return 0;
			}

			nfapi_p4_p5_message_header_t header;
			int unpack_result = nfapi_p5_message_header_unpack(header_buffer, header_buffer_size, &header, sizeof(header), 0);
			if(unpack_result < 0)
			{
				NFAPI_TRACE(NFAPI_TRACE_INFO, "VNF Failed to decode message header %d\n", unpack_result);
				return 0;
			}
			message_size = header.message_length;

			// now have the size of the mesage
		}

		if(message_size > stack_buffer_size)
		{
			dynamic_buffer = (uint8_t*)malloc(message_size);

			if(dynamic_buffer == NULL)
			{
				// todo : add error mesage
				NFAPI_TRACE(NFAPI_TRACE_INFO, "VNF Failed to allocate dynamic buffer for sctp_recvmsg size:%d\n", message_size);
				return -1;
			}

			read_buffer = dynamic_buffer;
		}

		{
			int flags = 0;
			(void)memset(&sndrcvinfo, 0, sizeof(struct sctp_sndrcvinfo));

			int recvmsg_result = sctp_recvmsg(pnf->p5_sock, read_buffer, message_size, (struct sockaddr*)&addr, &addr_len, &sndrcvinfo, &flags);
			if(recvmsg_result == -1)
			{
				NFAPI_TRACE(NFAPI_TRACE_INFO, "Failed to read sctp message size errno:%d\n", errno);
			}
			else
			{
				if (flags & MSG_NOTIFICATION)
				{
					NFAPI_TRACE(NFAPI_TRACE_INFO, "Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

					// todo - handle the events
				}
				else
				{
					/*
					NFAPI_TRACE(NFAPI_TRACE_INFO, "Received message fd:%d from %s:%u assoc:%d on stream %d, PPID %d, length %d, flags 0x%x\n",
							pnf->p5_sock,
							inet_ntoa(addr.sin_addr),
							ntohs(addr.sin_port),
							sndrcvinfo.sinfo_assoc_id,
							sndrcvinfo.sinfo_stream,
							ntohl(sndrcvinfo.sinfo_ppid),
							message_size,
							flags);
					*/

					// handle now if complete message in one or more segments
					if ((flags & 0x80) == 0x80)
					{
						// printf("\nVNF RECEIVES:\n");
						// for(int i=0; i<message_size; i++){
						// 	printf("%d", read_buffer[i]);
						// }
						// printf("\n");

						vnf_handle_p4_p5_message(read_buffer, message_size, pnf->p5_idx, config);
					}
					else
					{
						NFAPI_TRACE(NFAPI_TRACE_WARN, "sctp_recvmsg: unhandled mode with flags 0x%x\n", flags);

						// assume socket disconnected
						NFAPI_TRACE(NFAPI_TRACE_WARN, "Disconnected socket\n");
						socket_connected =  0;
					}


				}
			}
		}

		if(dynamic_buffer)
		{
			free(dynamic_buffer);
		}

		return socket_connected;
	}
}

static int vnf_send_p5_msg(nfapi_vnf_pnf_info_t* pnf, const void *msg, int len, uint8_t stream)
{
	// printf("\n MESSAGE SENT: \n");
	// for(int i=0; i<len; i++){
	// 	printf("%d", *(uint8_t *)(msg + i));
	// }
	// printf("\n");

	//NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s len:%d stream:%d\n", __FUNCTION__, len, stream);

	int result = sctp_sendmsg(pnf->p5_sock, msg, len, (struct sockaddr*)&pnf->p5_pnf_sockaddr, sizeof(pnf->p5_pnf_sockaddr),1, 0, stream, 0, 4);

	if(result != len)
	{
		if(result <  0)
		{
			// error
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "sctp sendto failed errno: %d\n", errno);
		}
		else
		{
			// did not send all the message
		}
	}

	return 0;
}

int vnf_nr_pack_and_send_p5_message(vnf_t* vnf, uint16_t p5_idx, nfapi_p4_p5_message_header_t* msg, uint16_t msg_len)
{
	nfapi_vnf_pnf_info_t* pnf = nfapi_vnf_pnf_list_find(&(vnf->_public), p5_idx);
	
	if(pnf)
	{
		// pack the message for transmission
		int packedMessageLength = 0;
    if (NFAPI_MODE == NFAPI_MODE_AERIAL) {
#ifdef ENABLE_AERIAL
      // In case it is a FAPI message, create 2 messages, one with nFAPI header for OAI PNF and one with no nFAPI header for Aerial
      // L1
      nfapi_p4_p5_message_header_t *msgFAPI = calloc(1, msg_len);
      memcpy(msgFAPI, msg, msg_len);
      // create FAPI tx_buffer
      uint8_t tx_messagebufferFAPI[sizeof(vnf->tx_message_buffer)];
      int packedMessageLengthFAPI = -1;
      packedMessageLengthFAPI =
          fapi_nr_p5_message_pack(msgFAPI, msg_len, tx_messagebufferFAPI, sizeof(tx_messagebufferFAPI), &vnf->_public.codec_config);
      return aerial_send_P5_msg(tx_messagebufferFAPI, packedMessageLengthFAPI, msg);
#else
      return 0;
#endif
    } else {
      packedMessageLength = nfapi_nr_p5_message_pack(msg,
                                                     msg_len,
                                                     vnf->tx_message_buffer,
                                                     sizeof(vnf->tx_message_buffer),
                                                     &vnf->_public.codec_config);

      if (packedMessageLength < 0) {
        NFAPI_TRACE(NFAPI_TRACE_ERROR, "nfapi_p5_message_pack failed with return %d\n", packedMessageLength);
        return -1;
      }
      // printf("msg id = 0x%02x, entire message length: %d\n", msg->message_id, packedMessageLength);
      // for (int i = 0; i < packedMessageLength; i++) {
      //   printf(" msg->msg_buf[%d] = 0x%02x\n", i, ((uint8_t *) vnf->tx_message_buffer)[i]);
      // }

      return vnf_send_p5_msg(pnf, vnf->tx_message_buffer, packedMessageLength, 0);
    }
  } else {
    NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() cannot find pnf info for p5_idx:%d\n", __FUNCTION__, p5_idx);
    return -1;
  }

}


int vnf_pack_and_send_p5_message(vnf_t* vnf, uint16_t p5_idx, nfapi_p4_p5_message_header_t* msg, uint16_t msg_len)
{
	nfapi_vnf_pnf_info_t* pnf = nfapi_vnf_pnf_list_find(&(vnf->_public), p5_idx);
	
	if(pnf)
	{
		// pack the message for transmission
		int packedMessageLength = nfapi_p5_message_pack(msg, msg_len, vnf->tx_message_buffer, sizeof(vnf->tx_message_buffer), &vnf->_public.codec_config);

		if (packedMessageLength < 0)
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "nfapi_p5_message_pack failed with return %d\n", packedMessageLength);
			return -1;
		}

		return vnf_send_p5_msg(pnf, vnf->tx_message_buffer, packedMessageLength, 0/*msg->phy_id*/);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() cannot find pnf info for p5_idx:%d\n", __FUNCTION__, p5_idx);
		return -1;
	}
}


int vnf_pack_and_send_p4_message(vnf_t* vnf, uint16_t p5_idx, nfapi_p4_p5_message_header_t* msg, uint16_t msg_len)
{
	nfapi_vnf_pnf_info_t* pnf = nfapi_vnf_pnf_list_find(&(vnf->_public), p5_idx);
	
	if(pnf)
	{
		// pack the message for transmission
		int packedMessageLength = nfapi_p4_message_pack(msg, msg_len, vnf->tx_message_buffer, sizeof(vnf->tx_message_buffer), &vnf->_public.codec_config);

		if (packedMessageLength < 0)
		{
			NFAPI_TRACE(NFAPI_TRACE_ERROR, "nfapi_p4_message_pack failed with return %d\n", packedMessageLength);
			return -1;
		}

		return vnf_send_p5_msg(pnf, vnf->tx_message_buffer, packedMessageLength, 0/*msg->phy_id*/);
	}
	else
	{
		NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() cannot find pnf info for p5_idx:%d\n", __FUNCTION__, p5_idx);
		return -1;
	}
}

