/*
 *  Copyright (C) 2009, 2010
 *      Authors (alphabetical) :
 *		Klaus 'mrmoku' Kurzmann <mok@fluxnetz.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 */



#include <glib.h>
#include <freesmartphone.h>
#include <fsoframework.h>

#include "phoneui-utils-sim.h"
#include "dbus.h"
#include "helpers.h"

struct _sim_pack {
	void (*callback)(GError *, gpointer);
	gpointer data;
};

struct _sim_pb_info_pack {
	void (*callback)(GError *, int, int, int, gpointer);
	gpointer data;
};

struct _sim_contact_get_pack {
	void (*callback)(GError *, const char *, const char *, gpointer);
	gpointer data;
};

struct _sim_contacts_get_pack {
	void (*callback)(GError *, FreeSmartphoneGSMSIMEntry *, int, gpointer);
	gpointer data;
};

struct _sim_auth_status_pack {
	gpointer data;
	void (*callback)(GError *, FreeSmartphoneGSMSIMAuthStatus, gpointer);
};

static void
_sim_contact_delete_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	struct _sim_pack *pack = data;

	free_smartphone_gsm_sim_delete_entry_finish
			((FreeSmartphoneGSMSIM *)source, res, &error);

	if (pack->callback) {
		pack->callback(error, pack->data);
	}
	if (error) {
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

/*
 * Deletes contact index from SIM
 */
int
phoneui_utils_sim_contact_delete(const char *category, const int index,
				void (*callback)(GError *, gpointer),
				gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_pack *pack;

	pack = malloc(sizeof(*pack));
	pack->callback = callback;
	pack->data = data;
	proxy = _fso_gsm_sim();
	free_smartphone_gsm_sim_delete_entry
		(proxy, category, index, _sim_contact_delete_callback, data);

	return 0;
}

static void
_sim_contact_store_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	struct _sim_pack *pack = data;

	free_smartphone_gsm_sim_store_entry_finish
			((FreeSmartphoneGSMSIM *)source, res, &error);
	if (pack->callback) {
		pack->callback(error, pack->data);
	}
	if (error) {
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

/*
 * Stores contact (name, number) for category to SIM at index
 */
int
phoneui_utils_sim_contact_store(const char *category, const int index,
				const char *name, const char *number,
				void (*callback) (GError *, gpointer),
				gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_pack *pack;

	pack = malloc(sizeof(*pack));
	pack->callback = callback;
	pack->data = data;
	proxy = _fso_gsm_sim();

	free_smartphone_gsm_sim_store_entry(proxy, category, index, name, number,
						 _sim_contact_store_callback, pack);
	return 0;
}

static void
_sim_retrieve_phonebook_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	int count;
	FreeSmartphoneGSMSIMEntry *contacts;
	struct _sim_contacts_get_pack *pack = data;

	contacts = free_smartphone_gsm_sim_retrieve_phonebook_finish
			((FreeSmartphoneGSMSIM *)source, res, &count, &error);
	pack->callback(error, contacts, count, pack->data);
	// FIXME: free contacts !!!
	if (error) {
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

void
phoneui_utils_sim_contacts_get(const char *category,
	void (*callback) (GError *, FreeSmartphoneGSMSIMEntry *, int, gpointer),
			       gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_contacts_get_pack *pack;

	if (!callback) {
		g_warning("phoneui_utils_sim_contacts_get without a callback!");
		return;
	}
	pack = malloc(sizeof(*pack));
	pack->callback = callback;
	pack->data = data;
	proxy = _fso_gsm_sim();

	g_message("Probing for contacts");
	/* as we don't know the exact max index... we just cheat and assume
	there won't be SIMs with more than 1000 entries */
	free_smartphone_gsm_sim_retrieve_phonebook(proxy, category, 1, 1000,
					_sim_retrieve_phonebook_callback, pack);
}

static void
_sim_pb_info_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	int slots, number_length, name_length;
	struct _sim_pb_info_pack *pack = data;

	free_smartphone_gsm_sim_get_phonebook_info_finish
			((FreeSmartphoneGSMSIM *)source, res, &slots,
			 &number_length, &name_length, &error);

	pack->callback(error, slots, number_length, name_length, pack->data);
	if (error) {
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

/*
 * Returns a GHashTable with the following values:
 * ("min_index", int:value) = lowest entry index for given phonebook on the SIM,
 * ("max_index", int:value) = highest entry index for given phonebook on the SIM,
 * ("number_length", int:value) = maximum length for the number,
 * ("name_length", int:value) = maximum length for the name.
 */
void
phoneui_utils_sim_phonebook_info_get(const char *category,
	void (*callback) (GError *, int, int, int, gpointer), gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_pb_info_pack *pack;

	pack = malloc(sizeof(*pack));
	pack->callback = callback;
	pack->data = data;
	proxy = _fso_gsm_sim();

	free_smartphone_gsm_sim_get_phonebook_info
				(proxy, category, _sim_pb_info_callback, pack);
}

static void
_sim_contact_get_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	int count;
	FreeSmartphoneGSMSIMEntry *contacts;
	struct _sim_contact_get_pack *pack = data;

	contacts = free_smartphone_gsm_sim_retrieve_phonebook_finish
			((FreeSmartphoneGSMSIM *)source, res, &count, &error);

	if (contacts) {
		pack->callback(error, contacts[0].name,
				 contacts[0].number, pack->data);
	}
	else {
		pack->callback(error, NULL, NULL, pack->data);
	}
	if (error) {
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

/*
 * Gets phonebook entry with index <index>
 */
void
phoneui_utils_sim_phonebook_entry_get(const char *category, const int index,
		void (*callback) (GError *, const char *name, const char *number, gpointer),
		gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_contact_get_pack *pack;

	pack = malloc(sizeof(*pack));
	pack->callback = callback;
	pack->data = data;
	proxy = _fso_gsm_sim();

	free_smartphone_gsm_sim_retrieve_phonebook
		(proxy, category, index, index, _sim_contact_get_callback, pack);
}

static void
_pin_send_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	struct _sim_pack *pack = data;

	free_smartphone_gsm_sim_send_auth_code_finish
			((FreeSmartphoneGSMSIM *)source, res, &error);
	g_debug("_auth_send_callback(%s)", error ? "ERROR" : "OK");
	if (pack->callback) {
		pack->callback(error, pack->data);
	}
	if (error) {
		g_debug("_auth_send_callback: (%d) %s",
			error->code, error->message);
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

void
phoneui_utils_sim_pin_send(const char *pin,
		void (*callback)(GError *, gpointer), gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_pack *pack;

	g_message("Trying PIN");
	pack = malloc(sizeof(*pack));
	pack->data = data;
	pack->callback = callback;
	proxy = _fso_gsm_sim();

	free_smartphone_gsm_sim_send_auth_code
			(proxy, pin, _pin_send_callback, pack);
}

static void
_puk_send_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	struct _sim_pack *pack = data;

	free_smartphone_gsm_sim_unlock_finish
			((FreeSmartphoneGSMSIM *)source, res, &error);

	if (pack->callback) {
		pack->callback(error, pack->data);
	}
	if (error) {
		g_warning("Sending PUK failed: (%d) %s",
			  error->code, error->message);
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

void
phoneui_utils_sim_puk_send(const char *puk, const char *new_pin,
		void (*callback)(GError *, gpointer), gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_pack *pack;

	g_debug("Trying PUK");
	pack = malloc(sizeof(*pack));
	pack->data = data;
	pack->callback = callback;
	proxy = _fso_gsm_sim();
	free_smartphone_gsm_sim_unlock
			(proxy, puk, new_pin, _puk_send_callback, pack);
}

static void
_get_auth_status_callback(GObject *source, GAsyncResult *res, gpointer data)
{
	GError *error = NULL;
	struct _sim_auth_status_pack *pack = data;
	FreeSmartphoneGSMSIMAuthStatus status;

	status = free_smartphone_gsm_sim_get_auth_status_finish
				((FreeSmartphoneGSMSIM *)source, res, &error);
	if (pack->callback) {
		pack->callback(error, status, pack->data);
	}

	if (error) {
		g_critical("Failed to get SIMAuthStatus: (%d) %s",
			   error->code, error->message);
		g_error_free(error);
	}
	g_object_unref(source);
	free(pack);
}

void
phoneui_utils_sim_auth_status_get(void (*callback)(GError *,
		FreeSmartphoneGSMSIMAuthStatus, gpointer), gpointer data)
{
	FreeSmartphoneGSMSIM *proxy;
	struct _sim_auth_status_pack *pack;

	pack = malloc(sizeof(*pack));
	pack->data = data;
	pack->callback = callback;
	proxy = _fso_gsm_sim();
	free_smartphone_gsm_sim_get_auth_status
				(proxy, _get_auth_status_callback, pack);
}
