/*
 *
 *  GattLib - GATT Library
 *
 *  Copyright (C) 2016-2020 Olivier Martin <olivier@labapart.org>
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "gattlib_internal.h"

#if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 40)

int gattlib_get_advertisement_data(gatt_connection_t *connection,
		gattlib_advertisement_data_t **advertisement_data, size_t *advertisement_data_count,
		uint16_t *manufacturer_id, uint8_t **manufacturer_data, size_t *manufacturer_data_size)
{
	return GATTLIB_NOT_SUPPORTED;
}

int gattlib_get_advertisement_data_from_mac(void *adapter, const char *mac_address,
		gattlib_advertisement_data_t **advertisement_data, size_t *advertisement_data_count,
		uint16_t *manufacturer_id, uint8_t **manufacturer_data, size_t *manufacturer_data_size)
{
	return GATTLIB_NOT_SUPPORTED;
}

#else

int get_advertisement_data_from_device(OrgBluezDevice1 *bluez_device1,
		gattlib_advertisement_data_t **advertisement_data, size_t *advertisement_data_count,
		uint16_t *manufacturer_id, uint8_t **manufacturer_data, size_t *manufacturer_data_size)
{
	GVariant *manufacturer_data_variant;
	GVariant *service_data_variant;

	if (advertisement_data == NULL) {
		return GATTLIB_INVALID_PARAMETER;
	}

	*manufacturer_id = 0;
	*manufacturer_data_size = 0;
	manufacturer_data_variant = org_bluez_device1_get_manufacturer_data(bluez_device1);
	if (manufacturer_data_variant != NULL) {
		if (g_variant_n_children(manufacturer_data_variant) != 1) {
			fprintf(stderr, "Warning: Manufacturer Data with multiple children: %s\n",
				g_variant_print(manufacturer_data_variant, TRUE));
			return GATTLIB_NOT_SUPPORTED;
		}
		GVariant* manufacturer_data_dict = g_variant_get_child_value(manufacturer_data_variant, 0);
		GVariantIter *iter;
		GVariant* values;

		g_variant_get(manufacturer_data_dict, "{qv}", manufacturer_id, &values);
		*manufacturer_data_size = g_variant_n_children(values);

		*manufacturer_data = calloc(*manufacturer_data_size, sizeof(guchar));
		if (*manufacturer_data == NULL) {
			return GATTLIB_OUT_OF_MEMORY;
		}

		GVariant* value;
		g_variant_get(values, "ay", &iter);
		size_t index = 0;

		while ((value = g_variant_iter_next_value(iter)) != NULL) {
			g_variant_get(value, "y", &(*manufacturer_data)[index++]);
			g_variant_unref(value);
		}
		g_variant_iter_free(iter);
	}

	service_data_variant = org_bluez_device1_get_service_data(bluez_device1);
	if (service_data_variant != NULL) {
		GVariantIter *iter;
		const gchar *key;
		GVariant *value;
		size_t index = 0;

		*advertisement_data_count = g_variant_n_children(service_data_variant);

		gattlib_advertisement_data_t *advertisement_data_ptr = calloc(sizeof(gattlib_advertisement_data_t), *advertisement_data_count);
		if (advertisement_data_ptr == NULL) {
			return GATTLIB_OUT_OF_MEMORY;
		}

		g_variant_get(service_data_variant, "a{sv}", &iter);
		while (g_variant_iter_loop(iter, "{&sv}", &key, &value)) {
			gattlib_string_to_uuid(key, strlen(key), &advertisement_data_ptr[index].uuid);

			gsize n_elements = 0;
			gconstpointer const_buffer = g_variant_get_fixed_array(value, &n_elements, sizeof(guchar));
			if (const_buffer) {
				advertisement_data_ptr[index].data = malloc(n_elements);
				if (advertisement_data_ptr[index].data == NULL) {
					return GATTLIB_OUT_OF_MEMORY;
				}

				advertisement_data_ptr[index].data_length = n_elements;
				memcpy(advertisement_data_ptr[index].data, const_buffer, n_elements);
			} else {
				advertisement_data_ptr[index].data_length = 0;
			}

			index++;
		}
		g_variant_iter_free(iter);

		*advertisement_data = advertisement_data_ptr;
	} else {
		*advertisement_data_count = 0;
		*advertisement_data = NULL;
	}

	return GATTLIB_SUCCESS;
}

int gattlib_get_advertisement_data(gatt_connection_t *connection,
		gattlib_advertisement_data_t **advertisement_data, size_t *advertisement_data_count,
		uint16_t *manufacturer_id, uint8_t **manufacturer_data, size_t *manufacturer_data_size)
{
	gattlib_context_t* conn_context;

	if (connection == NULL) {
		return GATTLIB_INVALID_PARAMETER;
	}

	conn_context = connection->context;

	return get_advertisement_data_from_device(conn_context->device,
			advertisement_data, advertisement_data_count,
			manufacturer_id, manufacturer_data, manufacturer_data_size);
}

int gattlib_get_advertisement_data_from_mac(void *adapter, const char *mac_address,
		gattlib_advertisement_data_t **advertisement_data, size_t *advertisement_data_count,
		uint16_t *manufacturer_id, uint8_t **manufacturer_data, size_t *manufacturer_data_size)
{
	OrgBluezDevice1 *bluez_device1;
	int ret;

	ret = get_bluez_device_from_mac(adapter, mac_address, &bluez_device1);
	if (ret != GATTLIB_SUCCESS) {
		g_object_unref(bluez_device1);
		return ret;
	}

	ret = get_advertisement_data_from_device(bluez_device1,
			advertisement_data, advertisement_data_count,
			manufacturer_id, manufacturer_data, manufacturer_data_size);

	g_object_unref(bluez_device1);

	return ret;
}

#endif /* #if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 40) */
