/*
 *
 * Dialog management module
 *
 * Copyright (C) 2005 João Filipe Plácido
 *
 * This file is part of openser, a free SIP server.
 *
 * openser 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
 *
 * openser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/*
 * History:
 * --------
 */




#include "../../sr_module.h"
#include <stdio.h>
#include "../tm/tm_load.h"
#include "../../ut.h"

MODULE_VERSION

struct tm_binds tmb;

static int mod_init(void);
static int terminate_dialog_f(struct sip_msg*, char*,char*);

/* the parameters are not used, they are only meant as an example*/
/* char* str_param;
int int_param;
*/




//struct dlg *dlgs;   /* Pointer to dlg table*/
struct dlg *dlg_uas = NULL;
struct dlg *dlg_uac = NULL;


static cmd_export_t cmds[]={
	{"terminate_dialog", terminate_dialog_f, 0, 0, REQUEST_ROUTE}, 
	{0, 0, 0, 0, 0}
};

static param_export_t params[]={ 
	{"str_param", STR_PARAM, &str_param},
	{"int_param", INT_PARAM, &int_param},
	{0,0,0} 
};

struct module_exports exports = {
	"dlgm", 
	cmds,
	params,
	
	mod_init, /* module initialization function */
	0,        /* response function*/
	0,        /* destroy function */
	0,        /* oncancel function */
	0         /* per-child init function */
};


/* ------------- Callback handlers --------------- */

static void dlgm_onreq( struct cell* t, int type, struct tmcb_params *ps );
static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps );



static int mod_init(void)
{
	fprintf(stderr, "dlgm - initializing\n");


	/* Initializing dlg table */
//        dlgs = (struct dlg *)shm_malloc(sizeof(struct dlg) * (32 + 1)); /* TODO define MAX_NO_OF_DIALOGS */
//        if (dlgs == 0) {
//            LOG(L_ERR, "ERROR: dlgm: mod_init(): "
//                "No memory for dlg table\n");
//            goto err;
//        }
//	dlg_uas = (struct dlg *)shm_malloc(sizeof(struct dlg));
//	dlg_uac = (struct dlg *)shm_malloc(sizeof(struct dlg));
//	memset(dlg_uas, 0, sizeof(struct dlg));
//        memset(dlg_uac, 0, sizeof(struct dlg));

        /* load the TM API */
        if (load_tm_api(&tmb)!=0) {
                LOG(L_ERR, "ERROR:dlgm:mod_init: can't load TM API\n");
                return -1;
        }

        /* register callbacks*/
        /* listen for all incoming requests  */
        if ( tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, dlgm_onreq, 0 ) <=0 ) {
                LOG(L_ERR,"ERROR:dlgm:mod_init: cannot register TMCB_REQUEST_IN "
                        "callback\n");
                return -1;
        }

	return 0;
err:
        return -1;

}


static void dlgm_bye_callback( struct cell *t, int type, struct tmcb_params *ps )
{
        printf("dlgm_bye_callback\n");
	/* destroy/free dialog */
	tmb.free_dlg(dlg_uac);
	tmb.free_dlg(dlg_uas);
	dlg_uac = NULL;
	dlg_uas = NULL;
}


static int terminate_dialog_f(struct sip_msg *msg, char *str1, char *str2) {
        printf("terminate_dialog()\n");
        // send BYE TO ALL PARTIES

        str method;
        int ret;

        method.s = "BYE";
        method.len = 3;
	
	if (dlg_uas!=NULL && dlg_uac!=NULL) {

	        printf("\n\n\ndlg_uas:\n");
        	tmb.print_dlg(stderr, dlg_uas);
	        printf("\n\n");
	        printf("dlg_uac:\n");
	        tmb.print_dlg(stderr, dlg_uac);
	        printf("\n\n");

		if (dlg_uac->state == DLG_CONFIRMED && dlg_uas->state == DLG_CONFIRMED) {
	
			printf("initiating BYE transaction to caller\n");	
	
			/* initiate BYE transaction to caller */
		        ret = tmb.t_request_within(&method, 0, 0, dlg_uas, dlgm_bye_callback, 0);
		
		        if (ret <= 0) {
				printf("terminate_dialog_f: error sending BYE to UAC\n");
		        }
		        // we just sent the bye to the party that created the dialog since the dlg structure was created with new_dlg_uas
		        // so we pretended to be the callee and sent the BYE to the caller
		
			printf("initiating BYE transaction to callee\n");

		        /* initiate BYE transaction to called party */
			ret = tmb.t_request_within(&method, 0, 0, dlg_uac, dlgm_bye_callback, 0);
		
		        if (ret <= 0) {
		                printf("terminate_dialog_f: error sending BYE to UAS\n");
		        }
		} else {
			// initiate CANCEL transaction
			printf("terminate_dialog_f: DIALOG IS NOT CONFIRMED. TODO: USE CANCEL TO TERMINATE IT!");
			//tmb.t_cancel(&(dlg_uas->id.call_id), &(dlg_uas->id.loc_tag));
		}
	}
}



static void dlgm_onreq( struct cell* t, int type, struct tmcb_params *ps )
{
	printf("\n\n\nCALLBACK FROM TM!!!!!!!!!!\n\n");

	int tmcb_types;

	/* install additional handlers */
	tmcb_types =
		/* get incoming replies ready for processing */
		TMCB_RESPONSE_IN;
	if (tmb.register_tmcb( 0, t, tmcb_types, tmcb_func, 0 )<=0) {
		LOG(L_ERR,"ERROR:dlgm:dlgm_onreq: cannot register additional "
			"callbacks\n");
		return;
	}


	if (dlg_uas != NULL && dlg_uac != NULL) {
		if (tmb.dlg_request_uas(dlg_uas, ps->req) < 0) {
			LOG(L_ERR, "dlgm_onreq(): Error updating dlg_uas structure\n");
	                tmb.free_dlg(dlg_uas);
			
	                return;
		} else {
			printf("\n\nupdated dlg_uas with received request:\n");
	                tmb.print_dlg(stderr, dlg_uas);
		}
	}
	

	// TESTING
//	if (ps->req->first_line.u.request.method_value == METHOD_OPTIONS) {
//		terminate_dialog_f(ps->req, 0, 0);
//	}

}


static inline void dlgm_onreply_in(struct cell *t, struct sip_msg *reply,
        int code, void *param) 
{

        printf("\n\nREPLY with code: %d\n",code);

        if (t->uas.request==0) {
                LOG(L_ERR, "ERROR: dlgm_onreply_in: 0 request\n");
                return;
        }

	if (tmb.new_dlg_uas(t->uas.request, code, &dlg_uas) < 0) {
		LOG(L_ERR, "dlgm_onreply_in(): Error creating dlg_uas structure\n");
                tmb.free_dlg(dlg_uas);
		return;
	} else {
		printf("\n\nNEW dlg_uas:\n");
		tmb.print_dlg(stderr, dlg_uas);
	}

	unsigned int cseq;
	str2int(&(get_cseq(t->uas.request)->number), &cseq);
//	if (get_cseq_value(t->uas.request, (unsigned int*)&cseq) < 0) {
//		LOG(L_ERR, "dlgm_onreply_in: error getting cseq number\n");
//		return;
//	}

	if (tmb.new_dlg_uac(&(dlg_uas->id.call_id), /*fromtag*/ &(dlg_uas->id.rem_tag), 
			cseq, 
			&dlg_uas->rem_uri, &dlg_uas->loc_uri, &dlg_uac) < 0) {
                LOG(L_ERR, "dlgm_onreply_in(): Error while creating new dlg_uac\n");
//                goto err;
		return;
        } else {
		printf("\n\nNEW dlg_uac:\n");
                tmb.print_dlg(stderr, dlg_uac);
        }


        if (tmb.dlg_response_uac(dlg_uac, reply) < 0) {
                LOG(L_ERR, "dlgm_onreply_in(): Error while updating dlg_uac structure\n");
                tmb.free_dlg(dlg_uac);
		return;
        } else {
		printf("\n\ndlg_uac updated with response:\n");
                tmb.print_dlg(stderr, dlg_uac);
        }

}


static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps )
{
        if (type&TMCB_RESPONSE_IN) {
                dlgm_onreply_in( t, ps->rpl, ps->code, ps->param);
        }
}


