/*
 * Kamailio osp module.
 *
 * This module enables Kamailio to communicate with an Open Settlement
 * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
 * defined standard for Inter-Domain VoIP pricing, authorization
 * and usage exchange.  The technical specifications for OSP
 * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
 *
 * Uli Abend was the original contributor to this module.
 *
 * Copyright (C) 2001-2005 Fhg Fokus
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio 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
 *
 * Kamailio 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 Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <osp/osptrans.h>
#include "../../core/dprint.h"
#include "osptoolkit.h"

static OSPTTHREADRETURN ospReportUsageWork(void *usagearg);

typedef struct _osp_usage
{
	OSPTTRANHANDLE ospvTransaction; /* Transaction handle */
	unsigned ospvReleaseCode;		/* Release code */
	unsigned ospvDuration;			/* Length of call */
	time_t ospvStartTime;			/* Call start time */
	time_t ospvEndTime;				/* Call end time */
	time_t ospvAlertTime;			/* Call alert time */
	time_t ospvConnectTime;			/* Call connect time */
	unsigned ospvIsPDDInfoPresent;	/* Is PDD Info present */
	unsigned ospvPostDialDelay;		/* Post Dial Delay */
	unsigned ospvReleaseSource;		/* EP that released the call */
} osp_usage;

/*
 * Get OSP transaction ID from transaction handle
 * param transaction OSP transaction headle
 * return OSP transaction ID
 */
unsigned long long ospGetTransactionId(OSPTTRANHANDLE transaction)
{
	OSPTTRANS *context = NULL;
	unsigned long long id = 0;
	int errorcode = OSPC_ERR_NO_ERROR;

	context = OSPPTransactionGetContext(transaction, &errorcode);

	if(errorcode == OSPC_ERR_NO_ERROR) {
		id = (unsigned long long)context->TransactionID;
	} else {
		LM_ERR("failed to extract transaction_id from transaction handle %d "
			   "(%d)\n",
				transaction, errorcode);
	}

	return id;
}

/*
 * Create a thread to report OSP usage
 * param ospvTransaction OSP transaction handle
 * param ospvReleaseCode Call release reason
 * param ospvDurating Call duration
 * param ospvStartTime Call start time
 * param ospvEndTime Call end time
 * param ospvAlertTime Call alert time
 * param ospvConnectTime Call connected  time
 * param ospvIsPDDInfoPresent If post dial delay information avaliable
 * param ospvPostDialDelay Post dial delay information
 * param ospvReleaseSource Which side release the call
 */
void ospReportUsageWrapper(OSPTTRANHANDLE ospvTransaction,
		unsigned ospvReleaseCode, unsigned ospvDuration, time_t ospvStartTime,
		time_t ospvEndTime, time_t ospvAlertTime, time_t ospvConnectTime,
		unsigned ospvIsPDDInfoPresent, unsigned ospvPostDialDelay,
		unsigned ospvReleaseSource)
{
	osp_usage *usage;
	OSPTTHREADID threadid;
	OSPTTHRATTR threadattr;
	int errorcode;

	LM_DBG("schedule usage report for '%llu'\n",
			ospGetTransactionId(ospvTransaction));

	usage = (osp_usage *)malloc(sizeof(osp_usage));

	usage->ospvTransaction = ospvTransaction;
	usage->ospvReleaseCode = ospvReleaseCode;
	usage->ospvDuration = ospvDuration;
	usage->ospvStartTime = ospvStartTime;
	usage->ospvEndTime = ospvEndTime;
	usage->ospvAlertTime = ospvAlertTime;
	usage->ospvConnectTime = ospvConnectTime;
	usage->ospvIsPDDInfoPresent = ospvIsPDDInfoPresent;
	usage->ospvPostDialDelay = ospvPostDialDelay;
	usage->ospvReleaseSource = ospvReleaseSource;

	OSPM_THRATTR_INIT(threadattr, errorcode);

	OSPM_SETDETACHED_STATE(threadattr, errorcode);

	OSPM_CREATE_THREAD(
			threadid, &threadattr, ospReportUsageWork, usage, errorcode);

	OSPM_THRATTR_DESTROY(threadattr);
}

/*
 * Report OSP usage thread function
 * param usagearg OSP usage information
 * return
 */
static OSPTTHREADRETURN ospReportUsageWork(void *usagearg)
{
	int i;
	const int MAX_RETRIES = 5;
	osp_usage *usage;
	int errorcode;

	usage = (osp_usage *)usagearg;

	OSPPTransactionRecordFailure(usage->ospvTransaction,
			(enum OSPEFAILREASON)usage->ospvReleaseCode);

	for(i = 1; i <= MAX_RETRIES; i++) {
		errorcode = OSPPTransactionReportUsage(usage->ospvTransaction,
				usage->ospvDuration, usage->ospvStartTime, usage->ospvEndTime,
				usage->ospvAlertTime, usage->ospvConnectTime,
				usage->ospvIsPDDInfoPresent, usage->ospvPostDialDelay,
				usage->ospvReleaseSource, (unsigned char *)"", 0, 0, 0, 0, NULL,
				NULL);

		if(errorcode == OSPC_ERR_NO_ERROR) {
			LM_DBG("reporte usage for '%llu'\n",
					ospGetTransactionId(usage->ospvTransaction));
			break;
		} else {
			LM_ERR("failed to report usage for '%llu' (%d) attempt '%d' of "
				   "'%d'\n",
					ospGetTransactionId(usage->ospvTransaction), errorcode, i,
					MAX_RETRIES);
		}
	}

	OSPPTransactionDelete(usage->ospvTransaction);

	free(usage);

	OSPTTHREADRETURN_NULL();
}
