/*
 * 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 - 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;
}

struct InternalHalProbedDevice *HalUtlAddProbedDeviceToProbedDeviceList(struct InternalHalProbedDevice **probedDevicesList)
{
	struct InternalHalProbedDevice *currentProbedDevice;

	if(! probedDevicesList)
		return NULL;

	currentProbedDevice = *probedDevicesList;
	if(! currentProbedDevice) {
		currentProbedDevice = (struct InternalHalProbedDevice *) calloc(1, sizeof(struct InternalHalProbedDevice));
		if(! currentProbedDevice)
			return NULL;

		*probedDevicesList = currentProbedDevice;
	}
	else {
		while(currentProbedDevice->next)
			currentProbedDevice = currentProbedDevice->next;

		currentProbedDevice->next = calloc(1, sizeof(struct InternalHalProbedDevice));
		if(! currentProbedDevice->next)
			return NULL;

		currentProbedDevice = currentProbedDevice->next;
	}

	return currentProbedDevice;
}

int HalUtlRemoveSelectedProbedDeviceFromList(struct InternalHalProbedDevice **probedDevicesList,
					     struct InternalHalProbedDevice *probedDeviceToRemove)
{
	struct InternalHalProbedDevice *currentProbedDevice, *matchingProbedDevice;

	if(! probedDevicesList || ! *probedDevicesList || ! probedDeviceToRemove)
		return -1;

	currentProbedDevice = *probedDevicesList;
	if(currentProbedDevice == probedDeviceToRemove) {
		*probedDevicesList = currentProbedDevice->next;
		matchingProbedDevice = currentProbedDevice;
	}
	else {
		while(currentProbedDevice && currentProbedDevice->next != probedDeviceToRemove)
			currentProbedDevice = currentProbedDevice->next;

		if(currentProbedDevice) {
			matchingProbedDevice = currentProbedDevice->next;
			currentProbedDevice->next = currentProbedDevice->next->next;
		}
		else {
			return -2;
		}
	}

	free(matchingProbedDevice->uid);

	if(matchingProbedDevice->deviceData) {
		free(matchingProbedDevice->deviceData->extendedCardNb);
		free(matchingProbedDevice->deviceData->cardId);
		free(matchingProbedDevice->deviceData->cardShortName);
		free(matchingProbedDevice->deviceData->cardLongName);
		free(matchingProbedDevice->deviceData->cardDriver);
		free(matchingProbedDevice->deviceData->cardMixerName);
		free(matchingProbedDevice->deviceData->cardComponents);
		free(matchingProbedDevice->deviceData->playbackDeviceId);
		free(matchingProbedDevice->deviceData->playbackDeviceName);

		free(matchingProbedDevice->deviceData);
	}

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

	free(matchingProbedDevice);

	return 0;
}

int HalUtlRemoveAllProbedDevicesFromList(struct InternalHalProbedDevice **probedDevicesList)
{
	int probedDevicesRemoved = 0;

	if(! probedDevicesList)
		return -1;

	if(! *probedDevicesList)
		return 0;

	while(*probedDevicesList) {
		if(HalUtlRemoveSelectedProbedDeviceFromList(probedDevicesList, *probedDevicesList))
			return -2;

		probedDevicesRemoved++;
	}

	return probedDevicesRemoved;
}

int HalUtlGetNumberOfProbedDevicesInList(struct InternalHalProbedDevice **probedDevicesList)
{
	int numberOfProbedDevices = 0;
	struct InternalHalProbedDevice *currentProbedDevice;

	if(! probedDevicesList)
		return -1;

	currentProbedDevice = *probedDevicesList;
	while(currentProbedDevice) {
		currentProbedDevice = currentProbedDevice->next;
		numberOfProbedDevices++;
	}

	return numberOfProbedDevices;
}

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

	if(! probedDevicesList || ! *probedDevicesList || ! uid)
		return NULL;

	currentProbedDevice = *probedDevicesList;
	while(currentProbedDevice) {
		if(! strcmp(uid, currentProbedDevice->uid))
			return currentProbedDevice;

		currentProbedDevice = currentProbedDevice->next;
	}

	return NULL;
}

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

	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", &currentProbedDeviceData->cardId,
				       "cardShortName", &currentProbedDeviceData->cardShortName,
				       "cardLongName", &currentProbedDeviceData->cardLongName,
				       "cardDriver", &currentProbedDeviceData->cardDriver,
				       "cardMixerName", &currentProbedDeviceData->cardMixerName,
				       "cardComponents", &currentProbedDeviceData->cardComponents,
				       "playbackDeviceNb", &currentProbedDeviceData->playbackDeviceNb,
				       "playbackDeviceId", &currentProbedDeviceData->playbackDeviceId,
				       "playbackDeviceName", &currentProbedDeviceData->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)) {
		free(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 InternalHalProbedDevice **probedDevicesList,
							  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(! probedDevicesList || ! *probedDevicesList) {
		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(probedDevicesList, 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;
}

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;
	}
}

json_object *HalUtlGetJsonArrayForSelectedDependencies(afb_api_t apiHandle,
						       struct InternalHalProbedDevice **probedDevicesList,
						       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(! probedDevicesList || ! *probedDevicesList) {
		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;
	}

	currentProbedDevice = *probedDevicesList;
	while(currentProbedDevice) {
		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);

		currentProbedDevice = currentProbedDevice->next;
	}

	return requestedDependenciesInfoJ;
}

json_object *HalUtlGetJsonArrayForAvailableDependencies(afb_api_t apiHandle,
							struct InternalHalProbedDevice **probedDevicesList,
							enum DependencyInfoJsonFormat jsonFormat)
{
	json_object *requestedDependenciesInfoJ;

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

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

	requestedDependenciesInfoJ = HalUtlGetJsonArrayForSelectedDependencies(apiHandle,
									       probedDevicesList,
									       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 InternalHalProbedDevice **probedDevicesList,
						  enum DependencyInfoJsonFormat jsonFormat)
{
	json_object *requestedDependenciesInfoJ;

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

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

	requestedDependenciesInfoJ = HalUtlGetJsonArrayForSelectedDependencies(apiHandle,
									       probedDevicesList,
									       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			       *
 ******************************************************************************/

struct InternalHalMixerData *HalUtlAddMixerDataToMixerDataList(struct InternalHalMixerData **mixerDataList)
{
	struct InternalHalMixerData *currentMixerData;

	if(! mixerDataList)
		return NULL;

	currentMixerData = *mixerDataList;
	if(! currentMixerData) {
		currentMixerData = (struct InternalHalMixerData *) calloc(1, sizeof(struct InternalHalMixerData));
		if(! currentMixerData)
			return NULL;

		*mixerDataList = currentMixerData;
	}
	else {
		while(currentMixerData->next)
			currentMixerData = currentMixerData->next;

		currentMixerData->next = calloc(1, sizeof(struct InternalHalMixerData));
		if(! currentMixerData->next)
			return NULL;

		currentMixerData = currentMixerData->next;
	}

	return currentMixerData;
}

int HalUtlRemoveSelectedMixerData(struct InternalHalMixerData **mixerDataList,
				  struct InternalHalMixerData *mixerDataToRemove)
{
	struct InternalHalMixerData *currentMixerData, *matchingMixerData;

	if(! mixerDataList || ! *mixerDataList || ! mixerDataToRemove)
		return -1;

	currentMixerData = *mixerDataList;
	if(currentMixerData == mixerDataToRemove) {
		*mixerDataList = currentMixerData->next;
		matchingMixerData = currentMixerData;
	}
	else {
		while(currentMixerData && currentMixerData->next != mixerDataToRemove)
			currentMixerData = currentMixerData->next;

		if(currentMixerData) {
			matchingMixerData = currentMixerData->next;
			currentMixerData->next = currentMixerData->next->next;
		}
		else {
			return -2;
		}
	}

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

	free(matchingMixerData);

	return 0;
}

int HalUtlRemoveAllMixerData(struct InternalHalMixerData **mixerDataList)
{
	int mixerDataRemoved = 0;

	if(! mixerDataList)
		return -1;

	while(*mixerDataList) {
		if(HalUtlRemoveSelectedMixerData(mixerDataList, *mixerDataList))
			return -2;

		mixerDataRemoved++;
	}

	return mixerDataRemoved;
}

int HalUtlGetNumberOfMixerDataInList(struct InternalHalMixerData **mixerDataList)
{
	int numberOfMixerData = 0;
	struct InternalHalMixerData *currentMixerData;

	if(! mixerDataList)
		return -1;

	currentMixerData = *mixerDataList;
	while(currentMixerData) {
		currentMixerData = currentMixerData->next;
		numberOfMixerData++;
	}

	return numberOfMixerData;
}

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

	if(! mixerDataList || ! *mixerDataList || ! verb || ! verbToCall)
		return NULL;

	currentMixerData = *mixerDataList;
	while(currentMixerData) {
		if(! strcmp(verb, currentMixerData->verb) &&
		   ! strcmp(verbToCall, currentMixerData->verbToCall) &&
		   ((! streamCardId && ! currentMixerData->streamCardId) ||
		    (streamCardId && currentMixerData->streamCardId && ! strcmp(streamCardId, currentMixerData->streamCardId))))
			return currentMixerData;

		currentMixerData = currentMixerData->next;
	}

	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 InternalHalMixerData **mixerDataList)
{
	json_object *mixerDataArrayJ, *currentMixerDataJ;

	struct InternalHalMixerData *currentMixerData;

	if(! apiHandle) {
		AFB_API_ERROR(apiHandle, "Api handle is not valid");
		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;
	}

	currentMixerData = *mixerDataList;
	while(currentMixerData) {
		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);

		currentMixerData = currentMixerData->next;
	}

	return mixerDataArrayJ;
}

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

struct HalData *HalUtlAddHalToHalList(struct HalData **halDataList)
{
	struct HalData *currentHalData;

	if(! halDataList)
		return NULL;

	currentHalData = *halDataList;
	if(! currentHalData) {
		currentHalData = (struct HalData *) calloc(1, sizeof(struct HalData));
		if(! currentHalData)
			return NULL;

		*halDataList = currentHalData;
	}
	else {
		while(currentHalData->next)
			currentHalData = currentHalData->next;

		currentHalData->next = calloc(1, sizeof(struct HalData));
		if(! currentHalData->next)
			return NULL;

		currentHalData = currentHalData->next;
	}

	return currentHalData;
}

int HalUtlRemoveSelectedHalFromList(struct HalData **halDataList, struct HalData *halToRemove)
{
	struct HalData *currentHalData, *matchingHal;

	if(! halDataList || ! *halDataList || ! halToRemove)
		return -1;

	currentHalData = *halDataList;
	if(currentHalData == halToRemove) {
		*halDataList = currentHalData->next;
		matchingHal = currentHalData;
	}
	else {
		while(currentHalData && currentHalData->next != halToRemove)
			currentHalData = currentHalData->next;

		if(currentHalData) {
			matchingHal = currentHalData->next;
			currentHalData->next = currentHalData->next->next;
		}
		else {
			return -2;
		}
	}

	free(matchingHal->apiName);
	free(matchingHal->sndCardPath);
	free(matchingHal->info);
	free(matchingHal->author);
	free(matchingHal->version);
	free(matchingHal->date);

	if(matchingHal->internal) {
		free(matchingHal->internalHalData->mixerApiName);
		free(matchingHal->internalHalData->prefix);
		
		HalUtlRemoveAllProbedDevicesFromList(&matchingHal->internalHalData->probedDevicesList);

		HalUtlRemoveAllMixerData(&matchingHal->internalHalData->streamsData);

		HalUtlFreeAlsaCtlsMap(matchingHal->internalHalData->alsaMapT);

		free(matchingHal->internalHalData);
	}

	free(matchingHal);

	return 0;
}

int HalUtlRemoveAllHalFromList(struct HalData **halDataList)
{
	int halRemoved = 0;

	if(! halDataList)
		return -1;

	while(*halDataList) {
		if(HalUtlRemoveSelectedHalFromList(halDataList, *halDataList))
			return -2;

		halRemoved++;
	}

	return halRemoved;
}

int HalUtlGetNumberOfHalInList(struct HalData **halDataList)
{
	int numberOfHal = 0;
	struct HalData *currentHalData;

	if(! halDataList)
		return -1;

	currentHalData = *halDataList;
	while(currentHalData) {
		currentHalData = currentHalData->next;
		numberOfHal++;
	}

	return numberOfHal;
}

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

	if(! halDataList || ! *halDataList || ! apiName)
		return NULL;

	currentHalData = *halDataList;
	while(currentHalData) {
		if(! strcmp(apiName, currentHalData->apiName))
			return currentHalData;

		currentHalData = currentHalData->next;
	}

	return NULL;
}

struct HalData *HalUtlSearchReadyHalDataByCardId(struct HalData **halDataList, int cardId)
{
	struct HalData *currentHalData;

	if(! halDataList || ! *halDataList)
		return NULL;

	currentHalData = *halDataList;
	while(currentHalData) {
		if(currentHalData->status == HAL_STATUS_READY && currentHalData->sndCardId == cardId)
			return currentHalData;

		currentHalData = currentHalData->next;
	}

	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;

	if(halMgrData->halDataList)
		HalUtlRemoveAllHalFromList(&halMgrData->halDataList);

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

	free(halMgrData);
}