/*---------------------------------------------------------------------------*\

	FILE....: CALL.CPP
	TYPE....: C++ Module
	AUTHOR..: David Rowe
	DATE....: 30/7/98

	Call progress module for VPB API.


         Voicetronix Voice Processing Board (VPB) Software

         Copyright (C) 1999-2007 Voicetronix www.voicetronix.com.au

         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 St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "mess.h"
#include "apifunc.h"
#include "vpbdial.h"
#include "call.h"
#include "timer.h"
#include "generic.h"
#include "mapdev.h"

#ifdef _OPENPRI
 #include "openpri.h"
#endif

#include <cassert>
#include <cstring>


#define	DEF_DIALTONES			1
#define	DEF_DIALTONE_TIMEOUT		6000
#define	DEF_INTER_RINGBACK_TIMEOUT	6000
#define	DEF_RINGBACK_TIMEOUT		10000
#define	DEF_ANSWER_TIMEOUT		30000
#define	DEF_ANSWER_TIMEOUT_MAX		120000
#define	DEF_NTONES			8

#define	SLEEPMS				20		// sleep time

// call progress state machine states

#define	IDLE				0
#define	WAIT_FOR_DIALTONE		1
#define	WAIT_FOR_RINGBACK		2
#define	WAIT_FOR_ANSWER			3

// Call History defines
#define CALLHIS_TIME			3*60
#define CALLHIS_TIME_MS			3*60*1000
#define CALLHIS_CALLS			2


typedef unsigned int uint;

struct callhis {
	unsigned long timestamp;
	char *dialstr;
	callhis *next;
};

typedef struct {
	uint	dialtones;		// number of dialtones
	uint	dialtone_timeout;	// wait for dial tone timeout in sec
	uint	inter_ringback_timeout;	// if ringback stops for this time, 
					// call is considered connected
	uint	ringback_timeout;	// time to wait for initial ringback in secs
	uint	answer_timeout;		// time to wait for answer after ringback 
					// detected
	VPB_TONE_MAP	tone_map[VPB_MAX_TONE_MAP];
	int	ntones;	                // num tones in tone map

	int	state;			// call progress state variable
	Timer	dialtone_timer;
	Timer	ringback_timer;
	Timer	inter_ringback_timer;
	Timer	answer_timer;

	int	ret_code;
	int	async;			// true if in async mode
	int	handle;
	char	dialstr[VPB_MAX_STR];

} CALL;


static CALL	                *call;		// ptr to state array
static callhis 			*history;	// Call history
static GENERIC_CRITICAL_SECTION	CallSect;
static int                      NumCh;

static VPB_TONE_MAP def_tone_map[] = {
	{VPB_BUSY,         VPB_CALL_DISCONNECT, 0},
	{VPB_DIAL,         VPB_CALL_DIALTONE,   0},
	{VPB_RINGBACK,     VPB_CALL_RINGBACK,   0},
	{VPB_BUSY,         VPB_CALL_BUSY,       0},
	{VPB_GRUNT,        VPB_CALL_GRUNT,      0},
	/* DR: this code enabled for Studio 308 testing
	{VPB_BUSY_308,     VPB_CALL_BUSY,       0},
	{VPB_BUSY_308,     VPB_CALL_DISCONNECT, 0},
	{VPB_RINGBACK_308, VPB_CALL_RINGBACK,   0},
	*/
	{0,                0,                   1}
};

// translation table for debug purposes via vpb_translate

static const char *term_str[] = {
	"Connected",
	"No Dial Tone",
	"No Ring Back",
	"Busy",
	"No Answer",
	"Disconnected",
	"Invalid Data"
};

// translation table for debug purposes via mprintf

static const char *state_str[] = {
	"Idle",
	"Wait for Dialtone",
	"Wait for Ringback",
	"Wait for Answer"
};

static const char *tone_str[] = {
	"Disconnect",
	"Dial Tone",
	"Ring Back",
	"Busy",
	"Grunt"
};


int tone_is(int handle, USHORT call_id, USHORT toned_id);
int tone_to_call_id(int handle, USHORT toned_id);
void dial_isdn(void *data);
static int check_call_history(char *dialstr);
static int check_call_tstamp(unsigned long tstamp);


/*--------------------------------------------------------------------------*\

	FUNCTION: call_open
	AUTHOR..: David Rowe
	DATE....: 30/7/98

	Initialises the call module.

\*--------------------------------------------------------------------------*/

void call_open(USHORT numch) {
	call = new CALL[numch];

	int i;
	for(i=0; i<numch; i++) {
		call[i].dialtones = DEF_DIALTONES;
		call[i].dialtone_timeout = DEF_DIALTONE_TIMEOUT;
		call[i].inter_ringback_timeout = DEF_INTER_RINGBACK_TIMEOUT;
		call[i].ringback_timeout = DEF_RINGBACK_TIMEOUT;
		call[i].answer_timeout = DEF_ANSWER_TIMEOUT;

		// default call  map

		memcpy(call[i].tone_map, def_tone_map, sizeof(def_tone_map));
		call[i].ntones = DEF_NTONES;

		call[i].state = IDLE;
		call[i].handle = i;
	}

	NumCh = numch;
	GenericInitializeCriticalSection(&CallSect);
}

/*--------------------------------------------------------------------------*\

	FUNCTION: call_close
	AUTHOR..: David Rowe
	DATE....: 27/7/98

	Closes the call module.

\*--------------------------------------------------------------------------*/

void call_close() {
	delete [] call;
	call = NULL;
	GenericDeleteCriticalSection(&CallSect);
}

/*---------------------------------------------------------------------------*\

	FUNCTION: vpb_set_call
	AUTHOR..: David Rowe
	DATE....: 29/7/98

	Function to set call progress parameters.

\*--------------------------------------------------------------------------*/

int WINAPI vpb_set_call(int handle, VPB_CALL *vpb_call)
{

	try {
		ValidHandleCheck(handle);
		GenericEnterCriticalSection(&CallSect);

		// validate tone map

		VPB_TONE_MAP *ptm = vpb_call->tone_map;
		USHORT map = 0;
		while(ptm->terminate != 1) {

			if (ptm->tone_id > VPB_MD)
				throw Wobbly(VPBAPI_CALL_BODGY_TONE_MAP);

			switch(ptm->call_id) {
				case VPB_CALL_DISCONNECT:
				case VPB_CALL_DIALTONE:
				case VPB_CALL_BUSY:
				case VPB_CALL_RINGBACK:
				case VPB_CALL_GRUNT:
				break;
				default:
				  throw Wobbly(VPBAPI_CALL_BODGY_TONE_MAP);
			}

			ptm++;

			map++;
			if (map > VPB_MAX_TONE_MAP)
				throw Wobbly(VPBAPI_CALL_MAX_TONE_MAP);
		}
		if((ptm->call_id != 0 ) && (ptm->tone_id != 0))
		   throw Wobbly(VPBAPI_CALL_BODGY_TONE_MAP);

		// copy into internal tone map

		memcpy(call[handle].tone_map, vpb_call->tone_map,
		       sizeof(VPB_TONE_MAP)*map);
		call[handle].ntones = map;

		// copy other params

		call[handle].dialtones = vpb_call->dialtones;
		call[handle].dialtone_timeout = vpb_call->dialtone_timeout;
		call[handle].ringback_timeout = vpb_call->ringback_timeout;
		if (vpb_call->answer_timeout > DEF_ANSWER_TIMEOUT_MAX )
			call[handle].answer_timeout = DEF_ANSWER_TIMEOUT_MAX;
		else
			call[handle].answer_timeout = vpb_call->answer_timeout;
		GenericLeaveCriticalSection(&CallSect);
	}
	catch(const Wobbly &w){
		return RunTimeError(w,"vpb_set_call");
	}
	return VPB_OK;
}

/*--------------------------------------------------------------------------*\

	FUNCTION: vpb_get_call
	AUTHOR..: David Rowe
	DATE....: 29/7/98

	Function to get current call progress parameters.

\*--------------------------------------------------------------------------*/

int WINAPI vpb_get_call(int handle, VPB_CALL *vpb_call)
{

	try {
		GenericEnterCriticalSection(&CallSect);
		ValidHandleCheck(handle);

		// copy into internal tone map

		memcpy(vpb_call->tone_map, call[handle].tone_map,
		       sizeof(VPB_TONE_MAP)*(call[handle].ntones+1));

		// copy other params

		vpb_call->dialtones = call[handle].dialtones;
		vpb_call->dialtone_timeout = call[handle].dialtone_timeout;
		vpb_call->ringback_timeout = call[handle].ringback_timeout;
		vpb_call->inter_ringback_timeout =
			call[handle].inter_ringback_timeout;
		vpb_call->answer_timeout = call[handle].answer_timeout;
		GenericLeaveCriticalSection(&CallSect);
	}
	catch(const Wobbly &w){
		return RunTimeError(w,"vpb_get_call");
	}
	return VPB_OK;
}

/*--------------------------------------------------------------------------*\

	FUNCTION: vpb_call_sync
	AUTHOR..: David Rowe
	DATE....: 29/7/98

	Place a call using call progress algorithm.

	Note: when this function is called, make sure dial tone has not
	been detected yet.

\*--------------------------------------------------------------------------*/

int WINAPI vpb_call_sync(int handle, char *dialstr)
{
	CALL	*c;

	USHORT  b,ch;           // board and channel for this handle
#ifdef _OPENPRI
	int     ch_state=0;
	OpenPri *PriDsp;
#endif

	try {
		ValidHandleCheck(handle);
		c = &call[handle];
		if (c->state != IDLE)
			throw Wobbly(VPBAPI_CALL_PROGRESS_ALREADY_ACTIVE);
		vpbdial_validate(dialstr);
		strcpy(c->dialstr, dialstr);

		// Check if it is an ISDN channel
		maphndletodev(handle, &b, &ch);
#ifdef _OPENPRI
		VPBREG  *pvpbreg = vpb_c->vpbreg(b);

		if (pvpbreg->model == VPB_PRI){
			mprintf("Got Pri card, checking channel state\n");
			PriDsp=(OpenPri *)pvpbreg->hostdsp;
			ch_state = PriDsp->chan_state(ch);
			mprintf("Channel [%d] is in state [%d]\n",ch,ch_state);
			if (ch_state == 0) {
				// Channel is IDLE
				GenericEnterCriticalSection(&CallSect);
				c->async = 0;
				GenericLeaveCriticalSection(&CallSect);
				dial_isdn((void *)c);
			}
			else {
				throw Wobbly(VPBAPI_CALL_PROGRESS_ALREADY_ACTIVE);
			}
		}
		else {
#endif

			// copy params to local storage

			GenericEnterCriticalSection(&CallSect);
			c->async = 0;
			c->state = WAIT_FOR_DIALTONE;
			c->dialtone_timer.start();
			GenericLeaveCriticalSection(&CallSect);

			// now wait for MMQ to signal its all over!

			while(c->state != IDLE)
				GenericSleep(SLEEPMS);
#ifdef _OPENPRI
		}
#endif

	}
	catch(const Wobbly &w){
		return RunTimeError(w,"vpb_call_sync");
	}
	return c->ret_code;
}

/*--------------------------------------------------------------------------*\

	FUNCTION: vpb_call_async
	AUTHOR..: David Rowe
	DATE....: 29/7/98

	Place a call using call progress algorithm, async version.

	Note: when this function is called, make sure dial tone has not
	been detected yet.

\*--------------------------------------------------------------------------*/

int WINAPI vpb_call_async(int handle, char *dialstr)
{
	CALL	*c;

	USHORT  b,ch;           // board and channel for this handle
#ifdef _OPENPRI
	int     ch_state=0;
	OpenPri *PriDsp;
#endif

	try {
		ValidHandleCheck(handle);
		c = &call[handle];
		if (c->state != IDLE)
			throw Wobbly(VPBAPI_CALL_PROGRESS_ALREADY_ACTIVE);
		vpbdial_validate(dialstr);
		strcpy(c->dialstr, dialstr);
		// Check if it is an ISDN channel
		maphndletodev(handle, &b, &ch);
#ifdef _OPENPRI
		VPBREG  *pvpbreg = vpb_c->vpbreg(b);

		if (pvpbreg->model == VPB_PRI){
			mprintf("Got Pri card, checking channel state\n");
			PriDsp=(OpenPri *)pvpbreg->hostdsp;
			ch_state = PriDsp->chan_state(ch);
			mprintf("Channel [%d] is in state [%d]\n",ch,ch_state);
			if (ch_state == 0) {
				// Channel is IDLE
				GenericEnterCriticalSection(&CallSect);
				c->async = 1;
				GenericLeaveCriticalSection(&CallSect);
				//dial_isdn((void *)c);
				Generic_beginthread(dial_isdn, 0, (void*)c);
			}
			else {
				throw Wobbly(VPBAPI_CALL_PROGRESS_ALREADY_ACTIVE);
			}
		}
		else {
#endif

			// copy params to local storage
			GenericEnterCriticalSection(&CallSect);
			c->async = 1;
			c->state = WAIT_FOR_DIALTONE;
			c->dialtone_timer.start();
			GenericLeaveCriticalSection(&CallSect);
#ifdef _OPENPRI
		}
#endif

	}
	catch(const Wobbly &w){
		return RunTimeError(w,"vpb_call_async");
	}
	return VPB_OK;
}

/*--------------------------------------------------------------------------*\

	FUNCTION: call_new_tone
	AUTHOR..: David Rowe
	DATE....: 29/7/98

	Called when a new tone is detected by MMQ to iterate call progress
	algorithms.  Works with sync and async modes of call progress.

\*--------------------------------------------------------------------------*/

void call_new_tone(int handle, int tone_id)
{
	CALL		*c;
	int			next_state;
	VPB_EVENT	e;

	c = &call[handle];
	GenericEnterCriticalSection(&CallSect);
	if (c->state == IDLE) {
		GenericLeaveCriticalSection(&CallSect);
		return;
	}

	// iterate call progress state machine

	next_state = c->state;
	mprintf("[%d] state: %s  tone_id: %s\n",handle,state_str[c->state],
		    tone_str[tone_to_call_id(handle,tone_id)]);

	switch(c->state) {

		case WAIT_FOR_DIALTONE:
			if (tone_is(handle, VPB_CALL_DIALTONE, tone_id)) {
				vpb_dial_async(c->handle, c->dialstr);
				c->ringback_timer.start();
				next_state = WAIT_FOR_RINGBACK;
			}
		break;

		case WAIT_FOR_RINGBACK:
			if (tone_is(handle, VPB_CALL_RINGBACK, tone_id)) {
				c->answer_timer.start();
				c->inter_ringback_timer.start();
				next_state = WAIT_FOR_ANSWER;
			}

			if (tone_is(handle, VPB_CALL_BUSY, tone_id)) {
				c->ret_code = VPB_CALL_BUSY;
				next_state = IDLE;
			}
		break;

		case WAIT_FOR_ANSWER:

			// We can detect an answer when a grunt is detected

			if (tone_is(handle, VPB_CALL_GRUNT, tone_id)) {
				c->ret_code = VPB_CALL_CONNECTED;
				next_state = IDLE;
			}

			// If we get a ringback reset ringback timer

			if (tone_is(handle, VPB_CALL_RINGBACK, tone_id)) {
				c->inter_ringback_timer.start();
			}

			// if we get disconnect

			if (tone_is(handle, VPB_CALL_DISCONNECT, tone_id)) {
				c->ret_code = VPB_CALL_DISCONNECTED;
				next_state = IDLE;
			}
		break;

		default:
			assert(0);
	}

	c->state = next_state;

	// if state is back to idle, we can finish call progress analysis
	// for async mode, this means post events.
	// In sync mode we will fall out due to c->state being set to idle

	if ((c->state == IDLE) && c->async) {
		e.handle = handle;
		e.type = VPB_CALLEND;
		e.data = c->ret_code;
		putevt(&e, 0);
	}

	GenericLeaveCriticalSection(&CallSect);
}

/*--------------------------------------------------------------------------*\

	FUNCTION: call_check_timer
	AUTHOR..: David Rowe
	DATE....: 29/7/98

	Called each MMQ iteration to check if any timers have expired
	in call progress analysis.

\*--------------------------------------------------------------------------*/

void call_check_timer()
{
	CALL		*c;
	int		next_state;
	VPB_EVENT	e;
	int		handle;

	GenericEnterCriticalSection(&CallSect);
	for(handle = 0; handle<NumCh; handle++) {
		c = &call[handle];
		if (c->state != IDLE) {

			// iterate call progress state machine based on timers

			next_state = c->state;

			switch( c->state )
			{
			    case WAIT_FOR_DIALTONE:
				if( c->dialtone_timer
				    .check_timeout_ms(c->dialtone_timeout) )
				{
					c->ret_code = VPB_CALL_NO_DIAL_TONE;
					next_state = IDLE;
				}
				break;

			    case WAIT_FOR_RINGBACK:
				if( c->ringback_timer
				    .check_timeout_ms(c->ringback_timeout) )
				{
					c->ret_code = VPB_CALL_NO_RING_BACK;
					next_state = IDLE;
				}
				break;

			    case WAIT_FOR_ANSWER:
				if( c->answer_timer
				    .check_timeout_ms(c->answer_timeout) )
				{
					c->ret_code = VPB_CALL_NO_ANSWER;
					next_state = IDLE;
				}

				if( c->inter_ringback_timer
				    .check_timeout_ms(c->inter_ringback_timeout) )
				{
					c->ret_code = VPB_CALL_CONNECTED;
					next_state = IDLE;
				}
				break;

			    default:
				assert(0);
			}

			c->state = next_state;

			// if state is back to idle, we can finish call 
			// progress analysis
			// for async mode, this means post events.
			// In sync mode we will fall out due to c->state 
			// being set to idle

			if ((c->state == IDLE) && c->async) {
				e.handle = handle;
				e.type = VPB_CALLEND;
				e.data = c->ret_code;
				putevt(&e, 0);
			}
		} // if (c->state ...

	} // for(handles=0; ....

	GenericLeaveCriticalSection(&CallSect);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: tone_is()
	AUTHOR...: David Rowe
	DATE.....: 23/4/98

	Returns true if the toned_id maps to the desired call_id.

\*--------------------------------------------------------------------------*/

int tone_is(int handle, USHORT call_id, USHORT toned_id)
{
	VPB_TONE_MAP *ptm = call[handle].tone_map;

	while(!ptm->terminate) {
		if ((ptm->tone_id == toned_id) && (ptm->call_id == call_id))
			return 1;
		ptm++;
	}

	// tone id unrecognised, ie not in tone map

	return 0;
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: tone_to_call_id()
	AUTHOR...: David Rowe
	DATE.....: 30/7/98

	Returns the call id given the tone_id.

\*--------------------------------------------------------------------------*/

int tone_to_call_id(int handle, USHORT toned_id)
{
	VPB_TONE_MAP *ptm = call[handle].tone_map;

	while(!ptm->terminate) {
		if ((ptm->tone_id == toned_id))
			return(ptm->call_id);
		ptm++;
	}

	// tone id unrecognised, ie not in tone map
	mprintf("tone id not recognised by call\n");

	return 0;
}

/*--------------------------------------------------------------------------*\

	FUNCTION: call_term
	AUTHOR..: David Rowe
	DATE....: 30/7/98

	Translates the terminate code to a string for debug purposes.

\*--------------------------------------------------------------------------*/

const char *call_term(int data)
{
	return (data > 5 || data < 0) ? term_str[6] : term_str[data];
}

/*--------------------------------------------------------------------------*\

	FUNCTION: check_call_history
	AUTHOR..: Ben Kramer
	DATE....: 03/03/2005

	Checks the number passed in to see if it has been called with in the
	call history specifications.

\*--------------------------------------------------------------------------*/

static int check_call_history(char *dialstr)
{
	callhis *hist = NULL;
	callhis *histlast = NULL;
	int	call_count=0;

	if (history == NULL){
		/* No calls on this handle */
		hist = new callhis;
		history = hist;
		hist->dialstr = new char[strlen(dialstr)+1];
		strcpy(hist->dialstr,dialstr);
		hist->timestamp = GenerictimeGetTime();
		hist->next=NULL;
		mprintf("Check_call_history: Adding new call!\n");
		return 1;
	}
	else {
		/* Need to check history */
		hist = history;
		while(hist != NULL){
			/* Check time stamp */
			if(check_call_tstamp(hist->timestamp)){
				/* Still valid */
				if (strcmp(hist->dialstr,dialstr)==0){
					call_count++;
				}
				histlast = hist;
				hist = hist->next;
			}
			else {
				mprintf("Check_call_history: Call expired, deleting it!\n");
				/* expired, delete it */
				/* Check if we have a previous one */
				if (histlast != NULL){
					/* Check if we have a next */
					if(hist->next != NULL){
						/* link them together */
						histlast->next = hist->next;
					}
					else {
						histlast->next=NULL;
					}
					delete hist->dialstr;
					delete hist;
					hist = histlast->next;
				}
				else {
					/* We are at the root */
					/* Check if we have a next */
					if(hist->next != NULL){
						/* link them together */
						history = hist->next;
					}
					else {
						history=NULL;
					}
					delete hist->dialstr;
					delete hist;
					hist = history;
				}
			}
		}
		if (call_count < CALLHIS_CALLS){
			hist = new callhis;
			if (histlast == NULL) {
				history = hist;
			}
			else{
				histlast->next=hist;
			}
			hist->dialstr = new char[strlen(dialstr)+1];
			strcpy(hist->dialstr,dialstr);
			hist->timestamp = GenerictimeGetTime();
			hist->next=NULL;
			mprintf("Check_call_history: Adding new call!\n");
			return 1;
		}
		else {
			/* cant make a call! */
			mprintf("Check_call_history: Cant make call!\n");
			return 0;
		}
	}
	return 0;
}

static int check_call_tstamp(unsigned long tstamp)
{
	unsigned long timenow;
	int	msecs;
	timenow = GenerictimeGetTime();
	msecs = timenow - tstamp;
	if (msecs > CALLHIS_TIME_MS){
		return 0;
	}
	return 1;
}

/*--------------------------------------------------------------------------*\

	FUNCTION: vpb_call_sync_jp
	AUTHOR..: David Rowe + Ben Kramer
	DATE....: 29/7/98 + 03/03/2005

	Place a call using call progress algorithm.
	Checks that you havent called it X times in Y minutes.

	Note: when this function is called, make sure dial tone has not
	been detected yet.

\*--------------------------------------------------------------------------*/

int WINAPI vpb_call_sync_jp(int handle, char *dialstr)
{
	CALL	*c;

	try {
		ValidHandleCheck(handle);
		c = &call[handle];
		if (c->state != IDLE)
			throw Wobbly(VPBAPI_CALL_PROGRESS_ALREADY_ACTIVE);

		vpbdial_validate(dialstr);

		if (check_call_history(dialstr)==0){
			throw Wobbly(VPBAPI_CALL_MAX_CALLS_PER_MIN);
		}

		strcpy(c->dialstr, dialstr);

		// copy params to local storage

		GenericEnterCriticalSection(&CallSect);
		c->async = 0;
		c->state = WAIT_FOR_DIALTONE;
		c->dialtone_timer.start();
		GenericLeaveCriticalSection(&CallSect);

		// now wait for MMQ to signal its all over!

		while(c->state != IDLE)
			GenericSleep(SLEEPMS);

	}
	catch(const Wobbly &w){
		return RunTimeError(w,"vpb_call_sync");
	}
	return c->ret_code;
}

/*--------------------------------------------------------------------------*\

	FUNCTION: vpb_call_async_jp
	AUTHOR..: David Rowe + Ben Kramer
	DATE....: 29/7/98 + 03/03/2005

	Place a call using call progress algorithm, async version.
	Checks that you havent called it X times in Y minutes.

	Note: when this function is called, make sure dial tone has not
	been detected yet.

\*--------------------------------------------------------------------------*/

int WINAPI vpb_call_async_jp(int handle, char *dialstr)
{
	CALL	*c;

	try {
		ValidHandleCheck(handle);
		c = &call[handle];
		if (c->state != IDLE)
			throw Wobbly(VPBAPI_CALL_PROGRESS_ALREADY_ACTIVE);
		vpbdial_validate(dialstr);

		if (check_call_history(dialstr)==0){
			throw Wobbly(VPBAPI_CALL_MAX_CALLS_PER_MIN);
		}

		strcpy(c->dialstr, dialstr);

		// copy params to local storage

		GenericEnterCriticalSection(&CallSect);
		c->async = 1;
		c->state = WAIT_FOR_DIALTONE;
		c->dialtone_timer.start();
		GenericLeaveCriticalSection(&CallSect);

	}
	catch(const Wobbly &w) {
		return RunTimeError(w,"vpb_call_async");
	}
	return VPB_OK;
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: dial_isdn()
	AUTHOR...: Ben Kramer
	DATE.....: xx/12/2004

	This does the dial etc for when we are on an ISDN channel.

\*--------------------------------------------------------------------------*/

void dial_isdn(void *data)
{
	CALL *c = (CALL*)data;
	char    cid[21]="";
	VPB_EVENT	e;
	int 	ret, run = 1;


	mprintf("call:call_isdn making a call!\n");
//	vpbdial_dial_async(c->handle, c->dialstr);
	vpb_isdn_call(c->handle,c->dialstr,cid,0,0);
//	c->state = WAIT_FOR_RINGBACK;

	while (run){
		ret = vpb_get_event_ch_sync(c->handle,&e,SLEEPMS);
		if (ret == VPB_OK){
			mprintf("call:dial_isdn got an event [%d]\n",e.type);
			switch(e.type){
			case VPB_ISDN_ANS:
				c->ret_code = VPB_CALL_CONNECTED;
				run=0;
				break;
			case VPB_ISDN_BUSY:
				if (e.data == 17) {
					c->ret_code = VPB_CALL_BUSY;
				}
				else {
					c->ret_code = VPB_CALL_DISCONNECTED;
				}
				run=0;
				break;
			default:
				break;
			}
		}
	}
	mprintf("call:call_isdn finished!\n");
	if (c->async){
		// Send appropriate message

		e.handle = c->handle;
		e.type = VPB_CALLEND;
		e.data = c->ret_code;
		putevt(&e, 0);
		// Terminate thread
		Generic_endthread();
	}
	else {
		return;
	}
}
