/*
 * Copyright (C) 2018 "IoT.bzh"
 * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
 *
 * 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.
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>

#include <wrap-json.h>

#include <afb/afb-binding.h>

#include "4a-hal-utilities-data.h"
#include "4a-hal-utilities-alsa-data.h"

/*******************************************************************************
 *	Internal Hal - Linked list cleaning function declaration	       *
 ******************************************************************************/

void HalUtlFreeSelectedProbedDeviceAllocation(struct InternalHalProbedDevice *probedDeviceToFree);
void HalUtlFreeSelectedMixerDataAllocation(struct InternalHalMixerData *mixerDataToFree);
void HalUtlFreeSelectedHalMapDataAllocation(struct InternalHalAlsaMap *halMapDataToFree);
void HalUtlFreeSelectedHalAllocation(struct HalData *halDataToFree);

/*******************************************************************************
 *	Internal Hal - Generic function for linked list			       *
 ******************************************************************************/

void *HalUtlAddNodeInList(struct cds_list_head *listHead, enum LinkedListType listType)
{
	void *returned = NULL;
	struct cds_list_head *nodeToAdd;

	if(! listHead)
		return NULL;

	switch(listType) {
		case LINKED_LIST_FOR_DEPENDENCIES_DATA:
			returned = calloc(1, sizeof(struct InternalHalProbedDevice));
			if(! returned)
				return NULL;
			nodeToAdd = &((struct InternalHalProbedDevice *) returned)->node;
			break;

		case LINKED_LIST_FOR_MIXER_DATA:
			returned = calloc(1, sizeof(struct InternalHalMixerData));
			if(! returned)
				return NULL;
			nodeToAdd = &((struct InternalHalMixerData *) returned)->node;
			break;

		case LINKED_LIST_FOR_HALMAP_DATA:
			returned = calloc(1, sizeof(struct InternalHalAlsaMap));
			if(! returned)
				return NULL;
			nodeToAdd = &((struct InternalHalAlsaMap *) returned)->node;
			break;

		case LINKED_LIST_FOR_HAL_DATA:
			returned = calloc(1, sizeof(struct HalData));
			if(! returned)
				return NULL;
			nodeToAdd = &((struct HalData *) returned)->node;
			break;

		default:
			return NULL;
	}

	cds_list_add_tail(nodeToAdd, listHead);

	return returned;
}

int HalUtlRemoveNodeFromList(struct cds_list_head *listHead, void *dataToRemove, enum LinkedListType listType)
{
	if(! listHead || cds_list_empty(listHead) || ! dataToRemove)
		return -1;

	switch(listType) {
		case LINKED_LIST_FOR_DEPENDENCIES_DATA:
			cds_list_del(&((struct InternalHalProbedDevice *) dataToRemove)->node);

			HalUtlFreeSelectedProbedDeviceAllocation((struct InternalHalProbedDevice *) dataToRemove);
			break;

		case LINKED_LIST_FOR_MIXER_DATA:
			cds_list_del(&((struct InternalHalMixerData *) dataToRemove)->node);

			HalUtlFreeSelectedMixerDataAllocation((struct InternalHalMixerData *) dataToRemove);
			break;

		case LINKED_LIST_FOR_HALMAP_DATA:
			cds_list_del(&((struct InternalHalAlsaMap *) dataToRemove)->node);

			HalUtlFreeSelectedHalMapDataAllocation((struct InternalHalAlsaMap *) dataToRemove);
			break;

		case LINKED_LIST_FOR_HAL_DATA:
			cds_list_del(&((struct HalData *) dataToRemove)->node);

			HalUtlFreeSelectedHalAllocation((struct HalData *) dataToRemove);
			break;

		default:
			return -2;
	}

	return 0;
}

int HalUtlRemoveAllNodeFromList(struct cds_list_head *listHead, enum LinkedListType listType)
{
	int nodesRemoved = 0;

	struct cds_list_head *nodeToRemove, *savedNode;

	if(! listHead)
		return -1;

	if(cds_list_empty(listHead))
		return 0;

	cds_list_for_each_safe(nodeToRemove, savedNode, listHead) {
		switch(listType) {
			case LINKED_LIST_FOR_DEPENDENCIES_DATA:
				if(HalUtlRemoveSelectedProbedDeviceFromList(listHead,
									    cds_list_entry(nodeToRemove, struct InternalHalProbedDevice, node)))
					return -2;
				break;

			case LINKED_LIST_FOR_MIXER_DATA:
				if(HalUtlRemoveSelectedMixerData(listHead,
								 cds_list_entry(nodeToRemove, struct InternalHalMixerData, node)))
					return -3;
				break;

			case LINKED_LIST_FOR_HALMAP_DATA:
				if(HalUtlRemoveSelectedHalMapData(listHead,
								  cds_list_entry(nodeToRemove, struct InternalHalAlsaMap, node)))
					return -4;
				break;

			case LINKED_LIST_FOR_HAL_DATA:
				if(HalUtlRemoveSelectedHalFromList(listHead,
								   cds_list_entry(nodeToRemove, struct HalData, node)))
					return -5;
				break;

			default:
				return -6;
		}

		nodesRemoved++;
	}

	return nodesRemoved;
}

int HalUtlGetNumberOfNodesInList(struct cds_list_head *listHead)
{
	int numberOfNodes = 0;
	struct cds_list_head *listNode;

	if(! listHead)
		return -1;

	if(cds_list_empty(listHead))
		return 0;

	cds_list_for_each(listNode, listHead)
		numberOfNodes++;

	return numberOfNodes;
}

/*******************************************************************************
 *	Internal Hal - Probed devices structure handling functions	       *
 ******************************************************************************/

enum ProbedDeviceClasses HalUtlGetProbedDeviceClassFromString(char *probedDeviceString)
{
	if(! probedDeviceString)
		return INVALID_PROBED_DEVICE;

	if(! strcasecmp("static", probedDeviceString))
		return STATIC_PROBED_DEVICE;

	if(! strcasecmp("dynamic", probedDeviceString))
		return DYNAMIC_PROBED_DEVICE;

	if(! strcasecmp("mandatory", probedDeviceString))
		return MANDATORY_PROBED_DEVICE;

	return INVALID_PROBED_DEVICE;
}

char *HalUtlGetProbedDeviceClassString(enum ProbedDeviceClasses deviceClass)
{
	switch(deviceClass) {
		case STATIC_PROBED_DEVICE:
			return "static";

		case DYNAMIC_PROBED_DEVICE:
			return "dynamic";

		case MANDATORY_PROBED_DEVICE:
			return "mandatory";

		case INVALID_PROBED_DEVICE:
			return "invalid";

		default:
			return "unknown";
	}

	return NULL;
}

int HalUtlIsDependencySelected(struct InternalHalProbedDevice *probedDevice, enum DependencyStatus requestedStatus)
{
	if(! probedDevice) {
		return 0;
	}

	switch(requestedStatus) {
		case UNAVAILABLE_DEPENDENCY:
			if(! probedDevice->deviceData)
				return 1;
			else
				return 0;

		case AVAILABLE_DEPENDENCY:
			if(probedDevice->deviceData)
				return 1;
			else
				return 0;

		case ALL_DEPENDENCY:
			return 1;

		default:
			return 0;
	}
}

void HalUtlFreeSelectedProbedDeviceDataAllocation(struct InternalHalDeviceData *probedDeviceDataToFree)
{
	if(! probedDeviceDataToFree)
		return;

	free(probedDeviceDataToFree->extendedCardNb);
	free(probedDeviceDataToFree->cardId);
	free(probedDeviceDataToFree->cardShortName);
	free(probedDeviceDataToFree->cardLongName);
	free(probedDeviceDataToFree->cardDriver);
	free(probedDeviceDataToFree->cardMixerName);
	free(probedDeviceDataToFree->cardComponents);
	free(probedDeviceDataToFree->playbackDeviceId);
	free(probedDeviceDataToFree->playbackDeviceName);

	free(probedDeviceDataToFree);
}

void HalUtlFreeSelectedProbedDeviceAllocation(struct InternalHalProbedDevice *probedDeviceToFree)
{
	if(! probedDeviceToFree)
		return;

	free(probedDeviceToFree->uid);

	HalUtlFreeSelectedProbedDeviceDataAllocation(probedDeviceToFree->deviceData);

	if(probedDeviceToFree->requestedDeviceJ)
		json_object_put(probedDeviceToFree->requestedDeviceJ);

	free(probedDeviceToFree);
}

struct InternalHalProbedDevice *HalUtlAddProbedDeviceToProbedDeviceList(struct cds_list_head *probedDevicesListHead)
{
	return (struct InternalHalProbedDevice *) HalUtlAddNodeInList(probedDevicesListHead, LINKED_LIST_FOR_DEPENDENCIES_DATA);
}

int HalUtlRemoveSelectedProbedDeviceFromList(struct cds_list_head *probedDevicesListHead,
					     struct InternalHalProbedDevice *probedDeviceToRemove)
{
	return HalUtlRemoveNodeFromList(probedDevicesListHead, (void *) probedDeviceToRemove, LINKED_LIST_FOR_DEPENDENCIES_DATA);
}

int HalUtlRemoveAllProbedDevicesFromList(struct cds_list_head *probedDevicesListHead)
{
	return HalUtlRemoveAllNodeFromList(probedDevicesListHead, LINKED_LIST_FOR_DEPENDENCIES_DATA);
}

int HalUtlGetNumberOfProbedDevicesInList(struct cds_list_head *probedDevicesListHead)
{
	return HalUtlGetNumberOfNodesInList(probedDevicesListHead);
}

struct InternalHalProbedDevice *HalUtlSearchProbedDeviceDataById(struct cds_list_head *probedDevicesListHead,
								 char *uid)
{
	struct InternalHalProbedDevice *currentProbedDevice;

	if(! probedDevicesListHead || cds_list_empty(probedDevicesListHead) || ! uid)
		return NULL;

	cds_list_for_each_entry(currentProbedDevice, probedDevicesListHead, node) {
		if(! strcmp(uid, currentProbedDevice->uid))
			return currentProbedDevice;
	}

	return NULL;
}

int HalUtlGetProbedDeviceCardNbUsingUid(struct cds_list_head *probedDevicesListHead, char *uid)
{
	struct InternalHalProbedDevice *currentProbedDeviceData;

	currentProbedDeviceData = HalUtlSearchProbedDeviceDataById(probedDevicesListHead, uid);
	if(! currentProbedDeviceData ||
	   ! currentProbedDeviceData->deviceData)
		return HAL_UNKNOWN_DEVICE;

	return currentProbedDeviceData->deviceData->cardNb;
}

struct InternalHalDeviceData *HalUtlAllocateAndFillProbedDeviceDataUsingInfoGetResponse(json_object *responseJ)
{
	struct InternalHalDeviceData *currentProbedDeviceData;

	char *cardId,
	     *cardShortName,
	     *cardLongName,
	     *cardDriver,
	     *cardMixerName,
	     *cardComponents,
	     *playbackDeviceId = NULL,
	     *playbackDeviceName = NULL;

	if(! responseJ)
		return NULL;

	currentProbedDeviceData = calloc(1, sizeof(struct InternalHalDeviceData));
	if(! currentProbedDeviceData)
		return NULL;

	currentProbedDeviceData->cardNb = HAL_UNKNOWN_DEVICE;
	currentProbedDeviceData->playbackDeviceNb = HAL_UNKNOWN_DEVICE;

	if(wrap_json_unpack(responseJ, "{s:i, s:s, s:s, s:s, s:s, s:s, s:s, s?:i, s?:s, s?:s !}",
				       "cardNb", &currentProbedDeviceData->cardNb,
				       "cardId", &cardId,
				       "cardShortName", &cardShortName,
				       "cardLongName", &cardLongName,
				       "cardDriver", &cardDriver,
				       "cardMixerName", &cardMixerName,
				       "cardComponents", &cardComponents,
				       "playbackDeviceNb", &currentProbedDeviceData->playbackDeviceNb,
				       "playbackDeviceId", &playbackDeviceId,
				       "playbackDeviceName", &playbackDeviceName) ||
	   currentProbedDeviceData->cardNb == HAL_UNKNOWN_DEVICE ||
	   (currentProbedDeviceData->playbackDeviceNb == HAL_UNKNOWN_DEVICE &&
	    asprintf(&currentProbedDeviceData->extendedCardNb, "hw:%i", currentProbedDeviceData->cardNb) < 0) ||
	   (currentProbedDeviceData->playbackDeviceNb != HAL_UNKNOWN_DEVICE &&
	    asprintf(&currentProbedDeviceData->extendedCardNb, "hw:%i,%i",
							      currentProbedDeviceData->cardNb,
							      currentProbedDeviceData->playbackDeviceNb) < 0)) {
		HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
		return NULL;
	}

	currentProbedDeviceData->cardId = strdup(cardId);
	if(! currentProbedDeviceData->cardId) {
		HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
		return NULL;
	}

	currentProbedDeviceData->cardShortName = strdup(cardShortName);
	if(! currentProbedDeviceData->cardShortName) {
		HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
		return NULL;
	}

	currentProbedDeviceData->cardLongName = strdup(cardLongName);
	if(! currentProbedDeviceData->cardLongName) {
		HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
		return NULL;
	}

	currentProbedDeviceData->cardDriver = strdup(cardDriver);
	if(! currentProbedDeviceData->cardDriver) {
		HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
		return NULL;
	}

	currentProbedDeviceData->cardMixerName = strdup(cardMixerName);
	if(! currentProbedDeviceData->cardMixerName) {
		HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
		return NULL;
	}

	currentProbedDeviceData->cardComponents = strdup(cardComponents);
	if(! currentProbedDeviceData->cardComponents) {
		HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
		return NULL;
	}

	if(playbackDeviceId) {
		currentProbedDeviceData->playbackDeviceId = strdup(playbackDeviceId);
		if(! currentProbedDeviceData->playbackDeviceId) {
			HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
			return NULL;
		}
	}

	if(playbackDeviceName) {
		currentProbedDeviceData->playbackDeviceName = strdup(playbackDeviceName);
		if(! currentProbedDeviceData->playbackDeviceName) {
			HalUtlFreeSelectedProbedDeviceDataAllocation(currentProbedDeviceData);
			return NULL;
		}
	}

	return currentProbedDeviceData;
}

json_object *HalUtlGetCompactJsonForSpecificDependencies(afb_api_t apiHandle,
							 struct InternalHalProbedDevice *requestedProbedDevice)
{
	int wrapRet;

	char *cardNb;

	json_object *requestedProbedDeviceJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! requestedProbedDevice) {
		AFB_API_ERROR(apiHandle, "Requested dependency is not valid");
		return NULL;
	}

	if(! requestedProbedDevice->deviceData ||
	   requestedProbedDevice->deviceData->cardNb == HAL_UNKNOWN_DEVICE)
		cardNb = NULL;
	else
		cardNb = requestedProbedDevice->deviceData->extendedCardNb;

	wrapRet = wrap_json_pack(&requestedProbedDeviceJ,
				 "{s:s s:s}",
				 "uid", requestedProbedDevice->uid,
				 "cardNb", cardNb ? cardNb : "none");
	if(wrapRet) {
		AFB_API_ERROR(apiHandle,
			      "Didn't succeed to allocate (uid: '%s') dependency compact json object",
			      requestedProbedDevice->uid);
		return NULL;
	}

	return requestedProbedDeviceJ;
}

json_object *HalUtlGetFullJsonForSpecificDependencies(afb_api_t apiHandle,
						      struct InternalHalProbedDevice *requestedProbedDevice)
{
	int wrapRet;

	json_object *requestedProbedDeviceJ, *additionalInfoJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! requestedProbedDevice) {
		AFB_API_ERROR(apiHandle, "Requested dependency is not valid");
		return NULL;
	}

	wrapRet = wrap_json_pack(&requestedProbedDeviceJ,
				 "{s:s s:s s:b}",
				 "uid", requestedProbedDevice->uid,
				 "class", HalUtlGetProbedDeviceClassString(requestedProbedDevice->deviceClass),
				 "available", requestedProbedDevice->deviceData ? 1 : 0);
	if(wrapRet) {
		AFB_API_ERROR(apiHandle,
			      "Didn't succeed to allocate first part of requested (uid: '%s') dependency full json object",
			      requestedProbedDevice->uid);
		return NULL;
	}

	if(! requestedProbedDevice->deviceData ||
	   requestedProbedDevice->deviceData->cardNb == HAL_UNKNOWN_DEVICE)
		return requestedProbedDeviceJ;

	wrapRet = wrap_json_pack(&additionalInfoJ,
				 "{s:i s:s s:s s:s s:s s:s s:s s:s}",
				 "cardNb", requestedProbedDevice->deviceData->cardNb,
				 "cardId", requestedProbedDevice->deviceData->cardId,
				 "cardShortName", requestedProbedDevice->deviceData->cardShortName,
				 "cardLongName", requestedProbedDevice->deviceData->cardLongName,
				 "cardDriver", requestedProbedDevice->deviceData->cardDriver,
				 "cardMixerName", requestedProbedDevice->deviceData->cardMixerName,
				 "cardComponents", requestedProbedDevice->deviceData->cardComponents,
				 "extendedCardNb", requestedProbedDevice->deviceData->extendedCardNb);
	if(wrapRet) {
		AFB_API_ERROR(apiHandle,
			      "Didn't succeed to allocate second part of requested (uid: '%s') dependency full json object",
			      requestedProbedDevice->uid);
		json_object_put(requestedProbedDeviceJ);
		return NULL;
	}

	wrap_json_object_add(requestedProbedDeviceJ, additionalInfoJ);

	if(requestedProbedDevice->deviceData->playbackDeviceNb == HAL_UNKNOWN_DEVICE)
		return requestedProbedDeviceJ;

	wrapRet = wrap_json_pack(&additionalInfoJ,
				 "{s:i s:s s:s}",
				 "playbackDeviceNb", requestedProbedDevice->deviceData->playbackDeviceNb,
				 "playbackDeviceId", requestedProbedDevice->deviceData->playbackDeviceId,
				 "playbackDeviceName", requestedProbedDevice->deviceData->playbackDeviceName);
	if(wrapRet) {
		AFB_API_ERROR(apiHandle,
			      "Didn't succeed to allocate third part of requested (uid: '%s') dependency full json object",
			      requestedProbedDevice->uid);
		json_object_put(requestedProbedDeviceJ);
		return NULL;
	}

	wrap_json_object_add(requestedProbedDeviceJ, additionalInfoJ);

	return requestedProbedDeviceJ;
}

json_object *HalUtlGetJsonForSpecificDependencies(afb_api_t apiHandle,
						  struct InternalHalProbedDevice *requestedProbedDevice,
						  enum DependencyInfoJsonFormat jsonFormat)
{
	json_object *requestedProbedDeviceJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! requestedProbedDevice) {
		AFB_API_ERROR(apiHandle, "Requested dependency is not valid");
		return NULL;
	}

	switch(jsonFormat) {
		case DEPENDENCY_COMPACT_JSON:
			requestedProbedDeviceJ = HalUtlGetCompactJsonForSpecificDependencies(apiHandle,requestedProbedDevice);
			if(! requestedProbedDeviceJ) {
				AFB_API_ERROR(apiHandle,
					      "An error happened when generating compact json for selected dependency (uid: '%s')",
					      requestedProbedDevice->uid);
				return NULL;
			}
			return requestedProbedDeviceJ;

		case DEPENDENCY_FULL_JSON:
			requestedProbedDeviceJ = HalUtlGetFullJsonForSpecificDependencies(apiHandle,requestedProbedDevice);
			if(! requestedProbedDeviceJ) {
				AFB_API_ERROR(apiHandle,
					      "An error happened when generating full json for selected dependency (uid: '%s')",
					      requestedProbedDevice->uid);
				return NULL;
			}
			return requestedProbedDeviceJ;

		default:
			AFB_API_ERROR(apiHandle, "Unrecognized requested json format");
			return NULL;
	}
}

json_object *HalUtlGetJsonForSpecificDependenciesUsingUid(afb_api_t apiHandle,
							  struct cds_list_head *probedDevicesListHead,
							  char *uid,
							  enum DependencyInfoJsonFormat jsonFormat)
{
	json_object *requestedProbedDeviceJ;

	struct InternalHalProbedDevice *requestedProbedDevice;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! probedDevicesListHead || cds_list_empty(probedDevicesListHead)) {
		AFB_API_ERROR(apiHandle, "Probed device list is empty");
		return NULL;
	}

	if(! uid) {
		AFB_API_ERROR(apiHandle, "Requested dependency uid is not valid");
		return NULL;
	}

	requestedProbedDevice = HalUtlSearchProbedDeviceDataById(probedDevicesListHead, uid);
	if(! requestedProbedDevice) {
		AFB_API_ERROR(apiHandle,
			      "Requested dependency uid ('%s) was not found in probed device list",
			      uid);
		return NULL;
	}

	requestedProbedDeviceJ = HalUtlGetJsonForSpecificDependencies(apiHandle, requestedProbedDevice, jsonFormat);
	if(! requestedProbedDeviceJ) {
		AFB_API_ERROR(apiHandle,
			      "An error happened when generating json for selected dependency (uid: '%s')",
			      requestedProbedDevice->uid);
		return NULL;
	}

	return requestedProbedDeviceJ;
}

json_object *HalUtlGetJsonArrayForSelectedDependencies(afb_api_t apiHandle,
						       struct cds_list_head *probedDevicesListHead,
						       enum DependencyInfoJsonFormat jsonFormat,
						       enum DependencyStatus requestedStatus)
{
	json_object *requestedProbedDeviceJ, *requestedDependenciesInfoJ;

	struct InternalHalProbedDevice *currentProbedDevice;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! probedDevicesListHead || cds_list_empty(probedDevicesListHead)) {
		AFB_API_ERROR(apiHandle, "Probed device list is empty");
		return NULL;
	}

	requestedDependenciesInfoJ = json_object_new_array();
	if(! requestedDependenciesInfoJ) {
		AFB_API_ERROR(apiHandle, "Didn't succeed to allocate probed devices (dependencies) json array");
		return NULL;
	}

	cds_list_for_each_entry(currentProbedDevice, probedDevicesListHead, node) {
		requestedProbedDeviceJ = NULL;

		if(HalUtlIsDependencySelected(currentProbedDevice, requestedStatus)) {
			requestedProbedDeviceJ = HalUtlGetJsonForSpecificDependencies(apiHandle,
										      currentProbedDevice,
										      jsonFormat);
			if(! requestedProbedDeviceJ) {
				AFB_API_ERROR(apiHandle,
					      "Didn't succeed to get json for probed devices (dependencies) with uid : '%s'",
					      currentProbedDevice->uid);
				json_object_put(requestedDependenciesInfoJ);
				return NULL;
			}
		}

		if(requestedProbedDeviceJ)
			json_object_array_add(requestedDependenciesInfoJ, requestedProbedDeviceJ);
	}

	return requestedDependenciesInfoJ;
}

json_object *HalUtlGetJsonArrayForAvailableDependencies(afb_api_t apiHandle,
							struct cds_list_head *probedDevicesListHead,
							enum DependencyInfoJsonFormat jsonFormat)
{
	json_object *requestedDependenciesInfoJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! probedDevicesListHead || cds_list_empty(probedDevicesListHead)) {
		AFB_API_ERROR(apiHandle, "Probed device list is empty");
		return NULL;
	}

	requestedDependenciesInfoJ = HalUtlGetJsonArrayForSelectedDependencies(apiHandle,
									       probedDevicesListHead,
									       jsonFormat,
									       AVAILABLE_DEPENDENCY);
	if(! requestedDependenciesInfoJ) {
		AFB_API_ERROR(apiHandle, "Didn't succeed get available devices (dependencies) info");
		return NULL;
	}

	return requestedDependenciesInfoJ;
}

json_object *HalUtlGetJsonArrayForAllDependencies(afb_api_t apiHandle,
						  struct cds_list_head *probedDevicesListHead,
						  enum DependencyInfoJsonFormat jsonFormat)
{
	json_object *requestedDependenciesInfoJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! probedDevicesListHead || cds_list_empty(probedDevicesListHead)) {
		AFB_API_ERROR(apiHandle, "Probed device list is empty");
		return NULL;
	}

	requestedDependenciesInfoJ = HalUtlGetJsonArrayForSelectedDependencies(apiHandle,
									       probedDevicesListHead,
									       jsonFormat,
									       ALL_DEPENDENCY);
	if(! requestedDependenciesInfoJ) {
		AFB_API_ERROR(apiHandle, "Didn't succeed get available devices (dependencies) info");
		return NULL;
	}

	return requestedDependenciesInfoJ;
}

/*******************************************************************************
 *	Internal Hal - Streams data handling functions			       *
 ******************************************************************************/

void HalUtlFreeSelectedMixerDataAllocation(struct InternalHalMixerData *mixerDataToFree)
{
	if(! mixerDataToFree)
		return;

	free(mixerDataToFree->verb);
	free(mixerDataToFree->verbToCall);
	free(mixerDataToFree->streamCardId);

	free(mixerDataToFree);
}

struct InternalHalMixerData *HalUtlAddMixerDataToMixerDataList(struct cds_list_head *mixerDataListHead)
{
	return (struct InternalHalMixerData *) HalUtlAddNodeInList(mixerDataListHead, LINKED_LIST_FOR_MIXER_DATA);
}

int HalUtlRemoveSelectedMixerData(struct cds_list_head *mixerDataListHead,
				  struct InternalHalMixerData *mixerDataToRemove)
{
	return HalUtlRemoveNodeFromList(mixerDataListHead, (void *) mixerDataToRemove, LINKED_LIST_FOR_MIXER_DATA);
}

int HalUtlRemoveAllMixerData(struct cds_list_head *mixerDataListHead)
{
	return HalUtlRemoveAllNodeFromList(mixerDataListHead, LINKED_LIST_FOR_MIXER_DATA);
}

int HalUtlGetNumberOfMixerDataInList(struct cds_list_head *mixerDataListHead)
{
	return HalUtlGetNumberOfNodesInList(mixerDataListHead);
}

struct InternalHalMixerData *HalUtlSearchMixerDataByProperties(struct cds_list_head *mixerDataListHead,
							       char *verb,
							       char *verbToCall,
							       char *streamCardId)
{
	struct InternalHalMixerData *currentMixerData;

	if(! mixerDataListHead || cds_list_empty(mixerDataListHead) || ! verb || ! verbToCall)
		return NULL;

	cds_list_for_each_entry(currentMixerData, mixerDataListHead, node) {
		if(! strcmp(verb, currentMixerData->verb) &&
		   ! strcmp(verbToCall, currentMixerData->verbToCall) &&
		   ((! streamCardId && ! currentMixerData->streamCardId) ||
		    (streamCardId && currentMixerData->streamCardId && ! strcmp(streamCardId, currentMixerData->streamCardId))))
			return currentMixerData;
	}

	return NULL;
}

json_object *HalUtlGetJsonArrayForSpecificMixerData(afb_api_t apiHandle, struct InternalHalMixerData *mixerData)
{
	int wrapRet;

	json_object *currentMixerDataJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(! mixerData) {
		AFB_API_ERROR(apiHandle, "Mixer data to use to generate json object are empty");
		return NULL;
	}

	wrapRet = wrap_json_pack(&currentMixerDataJ,
				 "{s:s s:s}",
				 "name", mixerData->verb,
				 "cardId", mixerData->streamCardId);
	if(wrapRet) {
		AFB_API_ERROR(apiHandle, "Didn't succeed to allocate current mixer json object");
		return NULL;
	}

	return currentMixerDataJ;
}

json_object *HalUtlGetJsonArrayForAllMixersData(afb_api_t apiHandle, struct cds_list_head *mixerDataListHead)
{
	json_object *mixerDataArrayJ, *currentMixerDataJ;

	struct InternalHalMixerData *currentMixerData;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		return NULL;
	}

	if(cds_list_empty(mixerDataListHead)) {
		AFB_API_ERROR(apiHandle, "Mixer data list is empty");
		return NULL;
	}

	mixerDataArrayJ = json_object_new_array();
	if(! mixerDataArrayJ) {
		AFB_API_ERROR(apiHandle, "Didn't succeed to allocate requested mixer data json array");
		return NULL;
	}

	cds_list_for_each_entry(currentMixerData, mixerDataListHead, node) {
		currentMixerDataJ = HalUtlGetJsonArrayForSpecificMixerData(apiHandle, currentMixerData);
		if(! currentMixerDataJ) {
			AFB_API_ERROR(apiHandle, "Didn't succeed to generate current mixer data json object");
			json_object_put(mixerDataArrayJ);
			return NULL;
		}

		json_object_array_add(mixerDataArrayJ, currentMixerDataJ);
	}

	return mixerDataArrayJ;
}

/*******************************************************************************
 *	Internal Hal - Alsa Map data handling functions			       *
 ******************************************************************************/

void HalUtlFreeSelectedHalMapDataAllocation(struct InternalHalAlsaMap *halMapDataToFree)
{
	free(halMapDataToFree->uid);
	free(halMapDataToFree->info);

	free(halMapDataToFree->action);

	free(halMapDataToFree->ctl.name);

	if(halMapDataToFree->ctl.alsaCtlCreation) {
		free(halMapDataToFree->ctl.alsaCtlCreation->enums);
		free(halMapDataToFree->ctl.alsaCtlCreation->dbscale);
		free(halMapDataToFree->ctl.alsaCtlCreation);
	}
	else if(halMapDataToFree->ctl.alsaCtlProperties) {
		free(halMapDataToFree->ctl.alsaCtlProperties->enums);
		free(halMapDataToFree->ctl.alsaCtlProperties->dbscale);
		free(halMapDataToFree->ctl.alsaCtlProperties);
	}

	free(halMapDataToFree);
}

struct InternalHalAlsaMap *HalUtlAddHalMapDataToHalMapDataList(struct cds_list_head *halMapListHead)
{
	return (struct InternalHalAlsaMap *) HalUtlAddNodeInList(halMapListHead, LINKED_LIST_FOR_HALMAP_DATA);
}

int HalUtlRemoveSelectedHalMapData(struct cds_list_head *halMapListHead,
				   struct InternalHalAlsaMap *halMapDataToRemove)
{
	return HalUtlRemoveNodeFromList(halMapListHead, (void *) halMapDataToRemove, LINKED_LIST_FOR_HALMAP_DATA);
}

int HalUtlRemoveAllHalMapData(struct cds_list_head *halMapListHead)
{
	return HalUtlRemoveAllNodeFromList(halMapListHead, LINKED_LIST_FOR_HALMAP_DATA);
}

int HalUtlGetNumberOfHalMapDataInList(struct cds_list_head *halMapListHead)
{
	return HalUtlGetNumberOfNodesInList(halMapListHead);
}

json_object *HalUtGetJsonArrayForSpecificHalMapControl(afb_api_t apiHandle,
						       struct InternalHalAlsaMap *currentHalMapData)
{
	int wrapRet;

	json_object *currentAlsaMapDataJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Can't get current internal hal api handle");
		return NULL;
	}

	if(! currentHalMapData) {
		AFB_API_ERROR(apiHandle, "ALSA control data to use to generate json object are empty");
		return NULL;
	}

	wrapRet = wrap_json_pack(&currentAlsaMapDataJ,
				 "{s:s s:s}",
				 "name", currentHalMapData->uid,
				 "info", currentHalMapData->info ? currentHalMapData->info : "none");
	if(wrapRet) {
		AFB_API_ERROR(apiHandle, "Didn't succeed to allocate current streams json object");
		return NULL;
	}

	return currentAlsaMapDataJ;
}

json_object *HalUtGetJsonArrayForAllHalMapControls(afb_api_t apiHandle, struct cds_list_head *halMapListHead)
{
	struct InternalHalAlsaMap *currentHalMapData;

	json_object *halMapDataArrayJ, *currentHalMapDataJ;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Can't get current internal hal api handle");
		return NULL;
	}

	if(! halMapListHead || cds_list_empty(halMapListHead)) {
		AFB_API_ERROR(apiHandle, "HalMap data list is empty");
		return NULL;
	}

	halMapDataArrayJ = json_object_new_array();
	if(! halMapDataArrayJ) {
		AFB_API_ERROR(apiHandle, "Didn't succeed to allocate ALSA controls data json array");
		return NULL;
	}

	cds_list_for_each_entry(currentHalMapData, halMapListHead, node) {
		currentHalMapDataJ = HalUtGetJsonArrayForSpecificHalMapControl(apiHandle, currentHalMapData);
		if(! currentHalMapDataJ) {
			AFB_API_ERROR(apiHandle, "Didn't succeed to generate current HalMap data json object");
			json_object_put(halMapDataArrayJ);
			return NULL;
		}

		json_object_array_add(halMapDataArrayJ, currentHalMapDataJ);
	}

	return halMapDataArrayJ;
}

/*******************************************************************************
 *	Hal data handling functions					       *
 ******************************************************************************/

void HalUtlFreeSelectedHalAllocation(struct HalData *halDataToFree)
{
	if(! halDataToFree)
		return;

	if(halDataToFree->internal) {
		free(halDataToFree->internalHalData->mixerApiName);
		free(halDataToFree->internalHalData->prefix);

		HalUtlRemoveAllProbedDevicesFromList(&halDataToFree->internalHalData->probedDevicesListHead);

		HalUtlRemoveAllMixerData(&halDataToFree->internalHalData->streamsDataListHead);

		HalUtlRemoveAllHalMapData(&halDataToFree->internalHalData->halMapListHead);

		free(halDataToFree->internalHalData);
	}

	free(halDataToFree);
}

struct HalData *HalUtlAddHalToHalList(struct cds_list_head *halDataListHead)
{
	return (struct HalData *) HalUtlAddNodeInList(halDataListHead, LINKED_LIST_FOR_HAL_DATA);
}

int HalUtlRemoveSelectedHalFromList(struct cds_list_head *halDataListHead, struct HalData *halToRemove)
{
	return HalUtlRemoveNodeFromList(halDataListHead, (void *) halToRemove, LINKED_LIST_FOR_HAL_DATA);
}

int HalUtlRemoveAllHalFromList(struct cds_list_head *halDataListHead)
{
	return HalUtlRemoveAllNodeFromList(halDataListHead, LINKED_LIST_FOR_HAL_DATA);
}

int HalUtlGetNumberOfHalInList(struct cds_list_head *halDataListHead)
{
	return HalUtlGetNumberOfNodesInList(halDataListHead);
}

struct HalData *HalUtlSearchHalDataByApiName(struct cds_list_head *halDataListHead, char *apiName)
{
	struct HalData *currentHalData;

	if(! halDataListHead || cds_list_empty(halDataListHead) || ! apiName)
		return NULL;

	cds_list_for_each_entry(currentHalData, halDataListHead, node) {
		if(! strcmp(apiName, currentHalData->apiName))
			return currentHalData;
	}

	return NULL;
}

/*******************************************************************************
 *	Hal Manager data handling functions				       *
 ******************************************************************************/

int HalUtlInitializeHalMgrData(afb_api_t apiHandle, struct HalMgrData *halMgrData, char *apiName, char *info)
{
	if(! apiHandle || ! halMgrData || ! apiName || ! info)
		return -1;

	// Allocate and fill apiName and info strings
	halMgrData->apiName = strdup(apiName);
	if(! halMgrData->apiName)
		return -2;

	halMgrData->info = strdup(info);
	if(! halMgrData->info)
		return -3;

	halMgrData->apiHandle = apiHandle;

	return 0;
}

void HalUtlRemoveHalMgrData(struct HalMgrData *halMgrData)
{
	if(! halMgrData)
		return;

	HalUtlRemoveAllHalFromList(&halMgrData->halDataListHead);

	free(halMgrData->apiName);
	free(halMgrData->info);

	free(halMgrData);
}