#include <malloc.h>
#include <string.h>

#include "alsa-softmixer.h"
#include "alsa-transaction.h"
#include "wrap-json.h"


void AlsaMixerTransactionDelete(AlsaMixerTransaction * transaction) {
	free((char*)transaction->uid);
	free(transaction);
}

AlsaMixerTransaction * AlsaMixerTransactionNew(struct SoftMixerT_ * mixer, const char * uid) {
	AlsaMixerTransaction * transaction = (AlsaMixerTransaction *) malloc(sizeof(AlsaMixerTransaction));
	if (transaction == NULL)
		goto fail;

	CDS_INIT_LIST_HEAD(&transaction->item_list);
	transaction->uid = strdup(uid);
	if (transaction->uid == NULL) {
		goto fail_list;
	}

	transaction->mixer = mixer;
	cds_list_add(&transaction->transaction_node, &mixer->transactionList);

	return transaction;

fail_list:
	free(transaction);
fail:
	return NULL;
}

void AlsaMixerTransactionDataListDestroy(AlsaMixerTransaction* list) {
	free(list);
}

void AlsaMixerTransactionObjectForget(AlsaMixerTransaction* list, void * object) {
	AlsaMixerTransactionDataItem *item, *tmp;
	cds_list_for_each_entry_safe(item, tmp, &list->item_list, list_entry)
		if (item->object == object) {
			cds_list_del(&item->list_entry);
			free(item);
		}
	}

bool AlsaMixerTransactionObjectAdd(AlsaMixerTransaction* list, void* object, AlsaTransactionDestructor destructor) {
	bool ret = false;
	AlsaMixerTransactionDataItem * newItem = NULL;
	if (!list)
		goto fail;

	newItem = (AlsaMixerTransactionDataItem *) malloc(sizeof(AlsaMixerTransactionDataItem));
	if (newItem == NULL)
		goto fail;

	CDS_INIT_LIST_HEAD(&newItem->list_entry);
	newItem->object = object;
	newItem->destructor = destructor;
	cds_list_add(&newItem->list_entry, &list->item_list);

	ret = true;
fail:
	return ret;

}

bool AlsaMixerTransactionObjectAddTail(AlsaMixerTransaction* list, void* object, AlsaTransactionDestructor destructor) {
	bool ret = false;
	AlsaMixerTransactionDataItem * newItem = NULL;
	if (!list)
		goto fail;

	newItem = (AlsaMixerTransactionDataItem *) malloc(sizeof(AlsaMixerTransactionDataItem));
	if (newItem == NULL)
		goto fail;

	CDS_INIT_LIST_HEAD(&newItem->list_entry);
	newItem->object = object;
	newItem->destructor = destructor;
	cds_list_add_tail(&newItem->list_entry, &list->item_list);

	ret = true;
fail:
	return ret;

}

void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction* transaction) {
	AlsaMixerTransactionDataItem * item, *sav;

    AFB_ApiInfo(transaction->mixer->api, "%s for transaction %s", __func__, transaction->uid);

	cds_list_for_each_entry_safe(item, sav, &transaction->item_list, list_entry) {
		if (item->destructor)
			item->destructor(transaction->mixer, item->object);

		cds_list_del(&item->list_entry);
		free(item);
	}

    AFB_ApiInfo(transaction->mixer->api, "%s for transaction %s .. DONE !", __func__, transaction->uid);
}

void AlsaMixerTransactionVerbCB(AFB_ReqT request) {
	json_object *responseJ = NULL;
	AlsaMixerTransaction *transaction = (AlsaMixerTransaction*) afb_req_get_vcbdata(request);
	json_object *argsJ = afb_req_json(request);
	int error;
	char * action = NULL;
	const char * uid = NULL;

	error = wrap_json_unpack(argsJ, "{ss!}",
			"action", &action);

	if (error) {
		AFB_ReqFailF(request, "missing action", "%s: missing 'action' field: %s", transaction->uid, json_object_get_string(argsJ));
		goto fail;
	}

	uid = strdup(transaction->uid);
	if (!uid) {
		SOFTMIXER_NOMEM(transaction->mixer->api);
		goto fail;
	}

	if (strcmp(action, "remove") == 0) {
		AlsaMixerTransactionDoCleanup(transaction);

		// remove this transaction for the list of mixer
		cds_list_del(&transaction->transaction_node);

		error = afb_api_del_verb(transaction->mixer->api, transaction->uid, (void**)NULL);
		if (error) {
			AFB_ReqFail(request, "verb deletion" , "verb was not removed");
			goto fail;
		}

		AlsaMixerTransactionDelete(transaction);

	} else {
		AFB_ReqFailF(request, "unsupported action", "%s: unsupported action %s (supported ones are ['remove']", transaction->uid, action);
		goto fail;
	}

	responseJ=json_object_new_object();
	json_object_object_add(responseJ, "result", json_object_new_string("OK"));
	AFB_ReqSuccess(request, responseJ, uid);

fail:
	free((char*)uid);
	return;

}