/* $Id: sofia.c,v 1.1.2.3 2011/02/05 20:22:53 khorben Exp $ */
/* Copyright (c) 2011 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Phone */
/* 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, version 3 of the License.
 *
 * 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, see <http://www.gnu.org/licenses/>. */



#include <stdlib.h>
#ifdef DEBUG
# include <stdio.h>
#endif
#include <string.h>
#include <System.h>
#include <Phone/modem.h>
#include <sofia-sip/nua.h>
#include <sofia-sip/sip_header.h>
#include <sofia-sip/su_glib.h>
#include <sofia-sip/url.h>


/* Sofia */
/* private */
/* types */
typedef struct _Sofia
{
	su_home_t home[1];
	su_root_t * root;
	guint source;
	nua_t * nua;
} Sofia;


/* prototypes */
static int _sofia_init(ModemPlugin * modem);
static int _sofia_destroy(ModemPlugin * modem);
static int _sofia_start(ModemPlugin * modem, unsigned int retry);
static int _sofia_stop(ModemPlugin * modem);
static int _sofia_request(ModemPlugin * modem, ModemRequest * request);

/* callbacks */
static void _sofia_callback(nua_event_t event, int status, char const * phrase,
		nua_t * nua, nua_magic_t * magic, nua_handle_t * nh,
		nua_hmagic_t * hmagic, sip_t const * sip, tagi_t tags[]);


/* public */
/* variables */
ModemPlugin plugin =
{
	NULL,
	"Sofia",
	NULL,
	NULL,
	_sofia_init,
	_sofia_destroy,
	_sofia_start,
	_sofia_stop,
	_sofia_request,
	NULL,
	NULL
};


/* private */
/* functions */
/* sofia_init */
static int _sofia_init(ModemPlugin * modem)
{
	Sofia * sofia;
	GSource * gsource;

	if((sofia = object_new(sizeof(*sofia))) == NULL)
		return -1;
	memset(sofia, 0, sizeof(*sofia));
	modem->priv = sofia;
	su_init();
	su_home_init(sofia->home);
	if((sofia->root = su_glib_root_create(NULL)) == NULL)
	{
		_sofia_destroy(modem);
		return -1;
	}
	gsource = su_glib_root_gsource(sofia->root);
	sofia->source = g_source_attach(gsource, g_main_context_default());
	return 0;
}


/* sofia_destroy */
static int _sofia_destroy(ModemPlugin * modem)
{
	Sofia * sofia = modem->priv;

	_sofia_stop(modem);
	su_root_destroy(sofia->root);
	su_home_deinit(sofia->home);
	su_deinit();
	object_delete(sofia);
	return 0;
}


/* sofia_start */
static int _sofia_start(ModemPlugin * modem, unsigned int retry)
{
	Sofia * sofia = modem->priv;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
	if(sofia->nua != NULL) /* already started */
		return 0;
	if((sofia->nua = nua_create(sofia->root, _sofia_callback, sofia,
					TAG_NULL())) == NULL)
		return -1;
	nua_set_params(sofia->nua, TAG_NULL());
	return 0;
}


/* sofia_stop */
static int _sofia_stop(ModemPlugin * modem)
{
	Sofia * sofia = modem->priv;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
	if(sofia->source != 0)
		g_source_remove(sofia->source);
	sofia->source = 0;
	if(sofia->nua != NULL)
	{
		nua_shutdown(sofia->nua);
		nua_destroy(sofia->nua);
	}
	sofia->nua = NULL;
	return 0;
}


/* sofia_request */
static int _request_call(ModemPlugin * modem, ModemRequest * request);

static int _sofia_request(ModemPlugin * modem, ModemRequest * request)
{
	Sofia * sofia = modem->priv;

	switch(request->type)
	{
		case MODEM_REQUEST_CALL:
			return _request_call(modem, request);
	}
}

static int _request_call(ModemPlugin * modem, ModemRequest * request)
{
	Sofia * sofia = modem->priv;
	url_t * url;
	struct { nua_handle_t * handle; } * op;
	sip_to_t * to;

	if((url = url_make(sofia->home, request->call.number)) == NULL)
		return -1;
	if((op = su_zalloc(sofia->home, sizeof(*op))) == NULL)
		return -1; /* XXX free url? */
	if((to = sip_to_create(NULL, url)) == NULL)
		return -1; /* XXX free url and op? */
	to->a_display = "Private"; /* XXX look it up */
	if((op->handle = nua_handle(sofia->nua, op, SIPTAG_TO(to), TAG_END()))
			== NULL)
		return -modem->helper->error(modem->helper->modem,
				"Cannot create operation handle", 1);
	nua_invite(op->handle, /* other tags as needed ... */ TAG_END());
	return 0;
}


/* callbacks */
/* sofia_callback */
static void _sofia_callback(nua_event_t event, int status, char const * phrase,
		nua_t * nua, nua_magic_t * magic, nua_handle_t * nh,
		nua_hmagic_t * hmagic, sip_t const * sip, tagi_t tags[])
{
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
	switch(event)
	{
		case nua_i_error:
			/* FIXME report error */
			fprintf(stderr, "%03d %s\n", status, phrase);
			break;
		case nua_i_notify:
			/* FIXME report event */
			fprintf(stderr, "%03d %s\n", status, phrase);
			break;
		case nua_r_invite:
			if(status == 200)
				nua_ack(nh, TAG_END());
			else
				/* FIXME report error */
				fprintf(stderr, "%03d %s\n", status, phrase);
			break;
#ifdef DEBUG
		default:
			fprintf(stderr, "DEBUG: %s() %s%d%s: %03d \"%s\"\n",
					__func__, "event ", event,
					" not handled: ", status, phrase);
			break;
#endif
	}
}