/*
 * Copyright (C) 2019 "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 <json-c/json.h>

#include <urcu/list.h>

#include <afb/afb-binding.h>

#include <ctl-config.h>

#include "4a-hal-utilities-data.h"
#include "4a-hal-utilities-hal-api-handler.h"

/*******************************************************************************
 *		Add/Remove hal data and create/delete api functions	       *
 ******************************************************************************/

int HalUtlAddHalDataAndCreateHalApi(afb_api_t apiHandle,
				    struct HalMgrData *halMgrData,
				    CtlConfigT *ctrlConfig,
				    int (*preinit)(void*, afb_api_t ))
{
	struct HalData *currentHalData;

	if(! apiHandle || ! halMgrData || ! ctrlConfig) {
		AFB_API_ERROR(apiHandle, "Invalid argument(s)");
		return -1;
	}

	// Allocation of current internal hal data
	currentHalData = HalUtlAddHalToHalList(&halMgrData->halDataListHead);
	if(! currentHalData) {
		AFB_API_ERROR(apiHandle, "Didn't succeed to add hal to hal list");
		return -2;
	}

	currentHalData->apiName = (char *) ctrlConfig->api;

	// Stores hal data in controller config
	setExternalData(ctrlConfig, (void *) currentHalData);

	// Allocation of the structure that will be used to store internal hal data
	currentHalData->internalHalData = calloc(1, sizeof(struct InternalHalData));
	if(! currentHalData->internalHalData) {
		AFB_API_ERROR(apiHandle, "Didn't succeed to allocate internal hal data structure");
		return -3;
	}

	CDS_INIT_LIST_HEAD(&currentHalData->internalHalData->probedDevicesListHead);
	CDS_INIT_LIST_HEAD(&currentHalData->internalHalData->streamsDataListHead);
	CDS_INIT_LIST_HEAD(&currentHalData->internalHalData->halMapListHead);

	// Create one API
	if(! afb_api_new_api(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, preinit, ctrlConfig)) {
		AFB_API_ERROR(apiHandle, "An error occurred at '%s' internal hal api creation", ctrlConfig->api);
		return -4;
	}

	return 0;
}

int HalUtlRemoveHalDataAndDeleteHalApi(afb_api_t apiHandle,
				       struct HalData *halDataToDelete,
				       struct cds_list_head *halDataListHead)
{
	int err;

	if(! apiHandle || ! halDataToDelete || ! halDataListHead) {
		AFB_API_ERROR(apiHandle, "Invalid argument(s)");
		return -1;
	}

	afb_api_delete_api(halDataToDelete->internalHalData->apiHandle);
	
	err = HalUtlRemoveSelectedHalFromList(halDataListHead, halDataToDelete);
	if(err) {
		AFB_API_ERROR(apiHandle,
			      "Error %i happened while trying to remove '%s' internal hal data structure",
			      err,
			      halDataToDelete->apiName);
		return -2;
	}

	return 0;
}

int HalUtlRemoveAllHalDataAndDeleteAllHalApi(afb_api_t apiHandle,
					     struct cds_list_head *halDataListHead)
{
	int err;
	struct HalData *currentHalData, *savedHalData;

	if(! apiHandle || ! halDataListHead) {
		AFB_API_ERROR(apiHandle, "Invalid argument(s)");
		return -1;
	}

	cds_list_for_each_entry_safe(currentHalData, savedHalData, halDataListHead, node) {
		err = HalUtlRemoveHalDataAndDeleteHalApi(apiHandle, currentHalData, halDataListHead);
		if(err) {
			AFB_API_ERROR(apiHandle,
				      "Error %i happened while trying to delete '%s' api and to remove its internal hal data structure",
				      err,
				      currentHalData->apiName);
			return -2;
		}
	}

	return 0;
}

int HalUtlRemoveHalMgrDataAndDeleteAllHalApi(afb_api_t apiHandle,
					     struct HalMgrData *halMgrData)
{
	int err;

	if(! apiHandle || ! halMgrData) {
		AFB_API_ERROR(apiHandle, "Invalid argument(s)");
		return -1;
	}

	err = HalUtlRemoveAllHalDataAndDeleteAllHalApi(apiHandle, &halMgrData->halDataListHead);
	if(err) {
		AFB_API_ERROR(apiHandle,
			      "Error %i happened while trying to remove all internal hal data structure and to delete all hal api",
			      err);
		return -2;
	}

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

	free(halMgrData);

	return 0;
}