/*
 * 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 "4a-hal-utilities-data.h"
#include "4a-hal-utilities-alsa-data.h"

/*******************************************************************************
 *		Specfic Hal controller streams data handling functions	       *
 ******************************************************************************/

struct CtlHalMixerData *HalUtlAddMixerDataToMixerDataList(struct CtlHalMixerData **firstMixerData)
{
	struct CtlHalMixerData *currentMixerData;

	if(! firstMixerData)
		return NULL;

	currentMixerData = *firstMixerData;

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

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

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

		currentMixerData = currentMixerData->next;
	}

	return currentMixerData;
}

int8_t HalUtlRemoveSelectedMixerData(struct CtlHalMixerData **firstMixerData, struct CtlHalMixerData *mixerDataToRemove)
{
	struct CtlHalMixerData *currentMixerData, *matchingMixerData;

	if(! firstMixerData || ! mixerDataToRemove)
		return -1;

	currentMixerData = *firstMixerData;

	if(currentMixerData == mixerDataToRemove) {
		*firstMixerData = 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;
}

int64_t HalUtlRemoveAllMixerData(struct CtlHalMixerData **firstMixerData)
{
	int8_t ret;
	int64_t mixerDataRemoved = 0;

	while(*firstMixerData) {
		ret = HalUtlRemoveSelectedMixerData(firstMixerData, *firstMixerData);
		if(ret)
			return (int64_t) ret;

		mixerDataRemoved++;
	}

	return mixerDataRemoved;
}

int64_t HalUtlGetNumberOfMixerDataInList(struct CtlHalMixerData **firstMixerData)
{
	int64_t numberOfMixerData = 0;
	struct CtlHalMixerData *currentMixerData;

	if(! firstMixerData)
		return -1;

	currentMixerData = *firstMixerData;

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

	return numberOfMixerData;
}

struct CtlHalMixerData *HalUtlSearchMixerDataByProperties(struct CtlHalMixerData **firstMixerData, char *verb, char *verbToCall, char *streamCardId)
{
	struct CtlHalMixerData *currentMixerData;

	if(! firstMixerData || ! verb)
		return NULL;

	currentMixerData = *firstMixerData;

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

		currentMixerData = currentMixerData->next;
	}

	return NULL;
}

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

struct SpecificHalData *HalUtlAddHalApiToHalList(struct SpecificHalData **firstHalData)
{
	struct SpecificHalData *currentApi;

	if(! firstHalData)
		return NULL;

	currentApi = *firstHalData;

	if(! currentApi) {
		currentApi = (struct SpecificHalData *) calloc(1, sizeof(struct SpecificHalData));
		if(! currentApi)
			return NULL;

		*firstHalData = currentApi;
	}
	else {
		while(currentApi->next)
			currentApi = currentApi->next;

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

		currentApi = currentApi->next;
	}

	return currentApi;
}

int8_t HalUtlRemoveSelectedHalFromList(struct SpecificHalData **firstHalData, struct SpecificHalData *apiToRemove)
{
	struct SpecificHalData *currentApi, *matchingApi;

	if(! firstHalData || ! apiToRemove)
		return -1;

	currentApi = *firstHalData;

	if(currentApi == apiToRemove) {
		*firstHalData = currentApi->next;
		matchingApi = currentApi;
	}
	else {
		while(currentApi && currentApi->next != apiToRemove)
			currentApi = currentApi->next;

		if(currentApi) {
			matchingApi = currentApi->next;
			currentApi->next = currentApi->next->next;
		}
		else {
			return -2;
		}
	}

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

	if(matchingApi->internal) {
		HalUtlRemoveAllMixerData(&matchingApi->ctlHalSpecificData->ctlHalStreamsData);
		HalUtlRemoveAllMixerData(&matchingApi->ctlHalSpecificData->ctlHalPlaybacksData);
		HalUtlRemoveAllMixerData(&matchingApi->ctlHalSpecificData->ctlHalCapturesData);

		HalUtlFreeAlsaCtlsMap(matchingApi->ctlHalSpecificData->ctlHalAlsaMapT);

		free(matchingApi->ctlHalSpecificData);
	}

	free(matchingApi);

	return 0;
}

int64_t HalUtlRemoveAllHalFromList(struct SpecificHalData **firstHalData)
{
	int8_t ret;
	int64_t CtlHalApiRemoved = 0;

	while(*firstHalData) {
		ret = HalUtlRemoveSelectedHalFromList(firstHalData, *firstHalData);
		if(ret)
			return (int64_t) ret;

		CtlHalApiRemoved++;
	}

	return CtlHalApiRemoved;
}

int64_t HalUtlGetNumberOfHalInList(struct SpecificHalData **firstHalData)
{
	int64_t numberOfCtlHal = 0;
	struct SpecificHalData *currentApi;

	if(! firstHalData)
		return -1;

	currentApi = *firstHalData;

	while(currentApi) {
		currentApi = currentApi->next;
		numberOfCtlHal++;
	}

	return numberOfCtlHal;
}

struct SpecificHalData *HalUtlSearchHalDataByApiName(struct SpecificHalData **firstHalData, char *apiName)
{
	struct SpecificHalData *currentApi;

	if(! firstHalData || ! apiName)
		return NULL;

	currentApi = *firstHalData;

	while(currentApi) {
		if(! strcmp(apiName, currentApi->apiName))
			return currentApi;

		currentApi = currentApi->next;
	}

	return NULL;
}


struct SpecificHalData *HalUtlSearchReadyHalDataByCarId(struct SpecificHalData **firstHalData, int cardId)
{
	struct SpecificHalData *currentApi;

	if(! firstHalData)
		return NULL;

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

		currentApi = currentApi->next;
	}

	return NULL;
}

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

uint8_t HalUtlInitializeHalMgrData(AFB_ApiT apiHandle, struct HalMgrData *HalMgrGlobalData, char *apiName, char *info)
{
	if(! apiHandle || ! HalMgrGlobalData || ! apiName || ! info)
		return -1;

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

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

	HalMgrGlobalData->apiHandle = apiHandle;

	return 0;
}

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

	if(HalMgrGlobalData->first)
		HalUtlRemoveAllHalFromList(&HalMgrGlobalData->first);

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

	free(HalMgrGlobalData);
}