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

	FILE....: callgen.cpp
	TYPE....: C++ Console program
	AUTHOR..: Ben Kramer
	DATE....: xx/01/2005

	This program is used for testing Voicetronix. 
	


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

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

         Voicetronix Voice Processing Board (VPB) Software

         Copyright (C) 1999-2004 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
	 USA

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

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <stdarg.h>


#include "../../src/libvpb/vpbapi.h"
#include "../../src/libvpb/verbose.h"
#include "../kbhit.h"
#include "../threads.h"

/*  call states */
#define CALL_IDLE	0
#define CALL_WT_ANS	1
#define CALL_ANS	2
#define CALL_FIN	3
#define CALL_RING	4

/* Run states */
#define RUN_STOP 0
#define RUN_WAIT 1
#define RUN_GO	 2

/* Call time out time */
#define CALL_TIMEOUT	60*1000

void *call_gen_thread(void *pv);
void *call_rec_thread(void *pv);

int             threads_active; // the number of active threads.
pthread_mutex_t mutex;          // mutex to protect shared data
int             finito;         // flag to signal the program is finished.
float           gain;           // option software record gain
int		failed_calls;
int		passed_calls;
int		total_calls;

typedef struct {
	int	handle;
	char	cid[20];
	char	cn[20];
	int	state;
	int	run;
} CHAN_INFO;

void myprintf(const char *fmt, ...);

void call_failed(int handle,int progress);
void call_passed(int handle);


int main(int argc, char * argv[])
{

	int	finito=0;
	int	i,x,j;
	int	ret;
	CHAN_INFO *cinfo;
	pthread_t       *aport_thread;
	VPB_EVENT e;

	setvbuf(stdout, NULL, _IONBF , 0);

	failed_calls=0;
	passed_calls=0;
	total_calls=0;

	// count the cards and ports
	int num_cards = vpb_get_num_cards();
	int num_ch = 0;

	int ports_per_card[num_cards];

	for (int i = 0; i < num_cards; ++i)
		num_ch += (ports_per_card[i] = vpb_get_ports_per_card(i));

	cinfo = new CHAN_INFO[num_ch];
	aport_thread = new pthread_t[num_ch];
	myprintf("Found [%d] channels!\n",num_ch);

	// verbose(1);

	// now init each port and start one thread per port
        x=0;
        for(i=0; i<num_cards; i++) {
                for(j=0;j<ports_per_card[i];j++){
                        cinfo[x].handle = vpb_open(i,j);
			cinfo[x].state=CALL_IDLE;
			cinfo[x].run=RUN_WAIT;
			if (x >= (num_ch/2)){
				pthread_create(&aport_thread[x], NULL, call_rec_thread, (void*)&cinfo[x]);
			}
			else {
				pthread_create(&aport_thread[x], NULL, call_gen_thread, (void*)&cinfo[x]);
			}
                        x++;
                }
        }

	for(i=num_ch/2; i<num_ch;i++){
		cinfo[i].run=RUN_GO;
	}
	vpb_sleep(1000);
	for(i=0; i<num_ch/2;i++){
		cinfo[i].run=RUN_GO;
	}

	
	// do nothing in main loop until return pressed
	while((!kbhit())&&!finito){
		ret = vpb_get_event_count();
		if (ret > 10){
			myprintf("main: ****************************** We have tooo many events [%d] *********\n",ret);
		}
		ret = vpb_get_event_sync(&e,500);
		if (ret == VPB_OK) {
			myprintf("main: Got an event [%d] on [%d]\n",e.type,e.handle);
		}
	}

	myprintf("main: shutting down....\n");
	for(i=0; i<num_ch; i++) {
		cinfo[i].run=RUN_STOP;
	}
	while (threads_active >0){
		vpb_sleep(500);
		myprintf("main: Threads active [%d]\n",threads_active);
	}
	myprintf("main: all threads stopped...\n");
	for(i=0; i<num_ch; i++) {
		vpb_close(cinfo[i].handle);
	}
	myprintf("main: all ports closed!\n");
	return 0;
}

void *call_gen_thread(void *pv) {
	CHAN_INFO *cinfo = (CHAN_INFO *)pv;
	VPB_EVENT e;
	char	number[21],cid[21];
	int	ret;
	int	newstate;
	sprintf(number,"40722222%02d",cinfo->handle+1);
	sprintf(cid,"40711111%02d",cinfo->handle+1);
	char	collected_dtmf[30];
	int	dtmf_idx=0;
	int	call_progress=0;
	void	*call_timer;
	int	dtmf_state=0;

	pthread_mutex_lock(&mutex);
	threads_active++;
	pthread_mutex_unlock(&mutex);

	vpb_timer_open(&call_timer,cinfo->handle,0,CALL_TIMEOUT);
	myprintf("chan[%d]: Waiting for GO!\n",cinfo->handle);
	while(cinfo->run == RUN_WAIT){
		ret = vpb_get_event_count_ch(cinfo->handle);
		if (ret > 1){
			myprintf("chan[%d]: **************************** We have tooo many events [%d] *********\n",cinfo->handle,ret);
		}
		ret = vpb_get_event_ch_sync(cinfo->handle,&e,50);
	}
	myprintf("chan[%d]: Call Generator Started!\n",cinfo->handle);

	while(cinfo->run != RUN_STOP) {
		ret = vpb_get_event_count_ch(cinfo->handle);
		if (ret > 1){
			myprintf("chan[%d]: **************************** We have tooo many events [%d] *********\n",cinfo->handle,ret);
		}
		ret = vpb_get_event_ch_sync(cinfo->handle,&e,500);
		if (ret == VPB_OK){
			newstate = cinfo->state;
			switch(cinfo->state){
				case CALL_IDLE:
					switch(e.type){
						case VPB_DROP:
							/* Do nothing */
							break;
						case VPB_TONEDETECT:
							/* Do nothing */
							break;
	
						default:
							myprintf("chan[%d]: Don't know what to do with event [%d] when IDLE\n",cinfo->handle,e.type);
					}
					break;
				case CALL_WT_ANS:
					switch(e.type){
						case VPB_ISDN_ANS:
							myprintf("chan[%d]: Call was answered!\n",cinfo->handle);
							sleep(1);
							vpb_dial_sync(cinfo->handle,cid);
							newstate = CALL_ANS;
							dtmf_idx=0;
							break;
						case VPB_DROP:
							myprintf("chan[%d]: The caller hung up in WT_ANS \n",cinfo->handle);
							call_failed(cinfo->handle,call_progress);
							vpb_timer_stop(call_timer);
							newstate = CALL_IDLE;
							call_progress=0;
							dtmf_idx=0;
							collected_dtmf[dtmf_idx]=0;
							break;
						case VPB_TIMEREXP:
							myprintf("chan[%d]: Timer EXP in WT_ANS: got dtmf[%s] dtmf state[%d]\n",cinfo->handle,collected_dtmf,dtmf_state);
							call_failed(cinfo->handle,call_progress);
							call_progress=0;
							dtmf_idx=0;
							collected_dtmf[dtmf_idx]=0;
							vpb_timer_stop(call_timer);
							vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
							newstate=CALL_IDLE;
							break;
						case VPB_TONEDETECT:
							/* Do nothing */
							break;
						default:
							myprintf("chan[%d]: Don't know what to do with event [%d] when WT ANS\n",cinfo->handle,e.type);
					}
					break;
				case CALL_ANS:
					switch(e.type){
				
						case VPB_DTMF:
							if (dtmf_idx>29){
								dtmf_idx=0;
							}
							collected_dtmf[dtmf_idx]=e.data;
							dtmf_idx++;
							collected_dtmf[dtmf_idx]=0;
							if (strcmp(collected_dtmf,"88")==0){
								myprintf("chan[%d]: They got what we sent!\n",cinfo->handle);
								call_progress=1;
								dtmf_idx=0;
								dtmf_state=1;
							}
							else if (strcmp(collected_dtmf,number)==0){
								call_passed(cinfo->handle);
								vpb_dial_sync(cinfo->handle,"77");
								vpb_timer_stop(call_timer);
								sleep(1);
								dtmf_idx=0;
								collected_dtmf[dtmf_idx]=0;
								newstate = CALL_IDLE;
								call_progress=0;
								myprintf("chan[%d]: Going ONHOOK in ANS\n",cinfo->handle);
								vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
							}
							break;
						case VPB_DROP:
							myprintf("chan[%d]: The caller hung up in ANS\n",cinfo->handle);
							call_failed(cinfo->handle,call_progress);
							dtmf_idx=0;
							collected_dtmf[dtmf_idx]=0;
							vpb_timer_stop(call_timer);
							call_progress=0;
							newstate = CALL_IDLE;
							break;
						case VPB_TIMEREXP:
							myprintf("chan[%d]: Timer EXP in ANS: got dtmf[%s] dtmf state[%d]\n",cinfo->handle,collected_dtmf,dtmf_state);
							call_failed(cinfo->handle,call_progress);
							dtmf_idx=0;
							collected_dtmf[dtmf_idx]=0;
							call_progress=0;
							vpb_timer_stop(call_timer);
							newstate=CALL_IDLE;
							vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
							break;
						case VPB_TONEDETECT:
							//myprintf("chan[%d]: Got a tone type [%d]\n",cinfo->handle,e.data);
							break;
						case VPB_DTMF_DOWN:
							break;
						default:
							myprintf("chan[%d]: Don't know what to do with event [%d] when ANS\n",cinfo->handle,e.type);
					}
					break;
				case CALL_RING:
					switch(e.type){
						case VPB_TIMEREXP:
							myprintf("chan[%d]: Timer EXP in RING: got dtmf[%s] dtmf state[%d]\n",cinfo->handle,collected_dtmf,dtmf_state);
							call_failed(cinfo->handle,call_progress);
							dtmf_idx=0;
							collected_dtmf[dtmf_idx]=0;
							call_progress=0;
							vpb_timer_stop(call_timer);
							newstate=CALL_IDLE;
							vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
							break;
						default:
							myprintf("chan[%d]: Don't know what to do with event [%d] when RING\n",cinfo->handle,e.type);
					}
					break;
				default:
					myprintf("chan[%d]: Don't understand what state [%d] Im in\n",cinfo->handle,cinfo->state);
			}
			cinfo->state=newstate;
		}
		else if (cinfo->state == CALL_IDLE){
			/* We are idle, lets start a call */
			vpb_sleep((500*cinfo->handle));
			myprintf("chan[%d]: Making call from [%s] to [%s]!\n",cinfo->handle,cid,number);
			vpb_isdn_call(cinfo->handle,number,cid,0,0);
			cinfo->state = CALL_WT_ANS;
			vpb_timer_start(call_timer);
			dtmf_state=0;
		}
	}
	myprintf("chan[%d]: Call Generator Stopping!\n",cinfo->handle);
	/* Make sure we leave things tidy! */
	if (cinfo->state != CALL_IDLE){
		vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
		cinfo->state = CALL_IDLE;
	}
	vpb_timer_close(call_timer);

	pthread_mutex_lock(&mutex);
	threads_active--;
	pthread_mutex_unlock(&mutex);

	return NULL;
}

void *call_rec_thread(void *pv) {
	CHAN_INFO *cinfo = (CHAN_INFO *)pv;
	VPB_EVENT e;
	int newstate;
	int ret;
	VPB_CALL_INFO *call_info;
	char collected_dtmf[30];
	int  dtmf_idx =0;
	int  call_progress=0;
	void	*call_timer;
	int dtmf_state=0;

	pthread_mutex_lock(&mutex);
	threads_active++;
	pthread_mutex_unlock(&mutex);

	vpb_timer_open(&call_timer,cinfo->handle,0,CALL_TIMEOUT);

	call_info = new VPB_CALL_INFO;
	myprintf("chan[%d]: Waiting for GO!\n",cinfo->handle);
	while(cinfo->run == RUN_WAIT){
		ret = vpb_get_event_count_ch(cinfo->handle);
		if (ret > 1){
			myprintf("chan[%d]: **************************** We have tooo many events [%d] *********\n",cinfo->handle,ret);
		}
		ret = vpb_get_event_ch_sync(cinfo->handle,&e,50);
	}
	myprintf("chan[%d]: Call Receiver Started!\n",cinfo->handle);

	while(cinfo->run != RUN_STOP) {
		ret = vpb_get_event_count_ch(cinfo->handle);
		if (ret > 1){
			myprintf("chan[%d]: **************************** We have tooo many events [%d] *********\n",cinfo->handle,ret);
		}
		ret = vpb_get_event_ch_sync(cinfo->handle,&e,500);
		if (ret == VPB_OK){
			newstate = cinfo->state;
			switch(cinfo->state){
				case CALL_IDLE:
					if (e.type == VPB_RING){
						newstate = CALL_RING;
						vpb_timer_start(call_timer);
						vpb_isdn_get_cinfo(e.handle,call_info);
						myprintf("chan[%d]: New call! [%d]\n",e.handle,call_info);
						sleep(1);
						vpb_sethook_sync(e.handle,VPB_OFFHOOK);
						newstate = CALL_ANS;
						myprintf("chan[%d]: Answered call \n",e.handle);
						dtmf_idx=0;
						collected_dtmf[0]=0;
						dtmf_state=0;
					}
					else if (e.type == VPB_DROP){
						/* Do nothing! */
						myprintf("chan[%d]: DROP while idle\n",cinfo->handle);
					}
					else if (e.type == VPB_TONEDETECT){
						/* Do nothing! */
						myprintf("chan[%d]: TONE while idle\n",cinfo->handle);
					}
					else {
						myprintf("chan[%d]: Don't know what to do with event[%d] when IDLE\n",e.handle,e.type);
					}
					break;
				case CALL_ANS:
					if (e.type == VPB_DROP){
						vpb_record_terminate(e.handle);
						vpb_timer_stop(call_timer);
						newstate=CALL_IDLE;
						myprintf("chan[%d]: Caller hangup! Failed\n",e.handle);
						call_failed(cinfo->handle,call_progress);
						call_progress=0;
						dtmf_idx=0;
						collected_dtmf[dtmf_idx]=0;
					}
					else if (e.type == VPB_ISDN_CINFO){
						myprintf("chan[%d]: Got call information, from [%s] to [%s]!\n",e.handle,call_info->callingnum,call_info->callednum);
					}
					else if (e.type == VPB_TIMEREXP){
						myprintf("chan[%d]: Timer EXP in ANS: got dtmf[%s] dtmf state[%d]\n",cinfo->handle,collected_dtmf,dtmf_state);
						call_failed(cinfo->handle,call_progress);
						call_progress=0;
						dtmf_idx=0;
						collected_dtmf[dtmf_idx]=0;
						vpb_timer_stop(call_timer);
						vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
						newstate=CALL_IDLE;
					}
					else if (e.type == VPB_DTMF){
						collected_dtmf[dtmf_idx]=e.data;
						dtmf_idx++;
						collected_dtmf[dtmf_idx]=0;
						if (strcmp(call_info->callingnum,collected_dtmf)==0){
							myprintf("chan[%d]: We got the DTMF's we where looking for!\n",e.handle);
							dtmf_idx=0;
							call_progress=1;
							dtmf_state=1;
							vpb_dial_sync(cinfo->handle,"88");
							sleep(1);
							vpb_dial_sync(cinfo->handle,call_info->callednum);
						}
						else if (strcmp(collected_dtmf,"77")==0){
							call_progress=2;
							call_passed(cinfo->handle);
							dtmf_idx=0;
							collected_dtmf[dtmf_idx]=0;
							vpb_timer_stop(call_timer);
							myprintf("chan[%d]: Going ONHOOK in ANS\n",cinfo->handle);
							vpb_sethook_sync(e.handle,VPB_ONHOOK);
							newstate=CALL_IDLE;
							call_progress=0;
						}
						
					}
					else if (e.type == VPB_DIALEND){
						/* Do nothing! */
					}
					else if (e.type == VPB_TONEDETECT){
						myprintf("chan[%d]: Got a tone type [%d]\n",cinfo->handle,e.data);
					}
					else if (e.type == VPB_DTMF_DOWN){
						/* Do nothing! */
					}
					else {
						myprintf("chan[%d]: Don't know what to do with event[%d] when ANS\n",e.handle,e.type);
					}
					break;
				case CALL_RING:
					if (e.type == VPB_RING){
						vpb_sethook_sync(e.handle,VPB_OFFHOOK);
						newstate = CALL_ANS;
						myprintf("chan[%d]: Answered call \n",e.handle);
						dtmf_idx=0;
						collected_dtmf[0]=0;
						dtmf_state=0;
					}
					else if (e.type == VPB_ISDN_CINFO){
						myprintf("chan[%d]: Got call information, from [%s] to [%s]!\n",e.handle,call_info->callingnum,call_info->callednum);
					}
					else if (e.type == VPB_TIMEREXP){
						myprintf("chan[%d]: Timer EXP in RING: got dtmf[%s] dtmf state[%d]\n",cinfo->handle,collected_dtmf,dtmf_state);
						call_failed(cinfo->handle,call_progress);
						call_progress=0;
						dtmf_idx=0;
						collected_dtmf[dtmf_idx]=0;
						vpb_timer_stop(call_timer);
						vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
						newstate=CALL_IDLE;
					}
					else {
						myprintf("chan[%d]: Don't know what to do with event[%d] when RING\n",e.handle,e.type);
					}
					break;
				default:
					break;
			}
			cinfo->state = newstate;
		}
	}
	myprintf("chan[%d]: Call Receiver Stopping!\n",cinfo->handle);
	/* Make sure we leave things tidy! */
	if (cinfo->state != CALL_IDLE){
		vpb_record_terminate(cinfo->handle);
		vpb_sethook_sync(cinfo->handle,VPB_ONHOOK);
		cinfo->state = CALL_IDLE;
	}
	vpb_timer_close(call_timer);

	pthread_mutex_lock(&mutex);
	threads_active--;
	pthread_mutex_unlock(&mutex);

	return NULL;
}

void myprintf(const char *fmt, ...)
{
	va_list argptr;
	time_t  curtime;
	struct  tm *loctime;
	curtime = time (NULL);
	loctime = localtime (&curtime);
	printf("[%02d:%02d:%02d] ", loctime->tm_hour, loctime->tm_min, loctime->tm_sec);
	va_start(argptr, fmt);
	vprintf(fmt, argptr);
	va_end(argptr);
}

void call_passed(int handle)
{
	pthread_mutex_lock(&mutex);
	passed_calls++;
	total_calls++;
	myprintf("chan[%d]: PASS (%d) FAIL (%d) TOTAL(%d)\n",handle,passed_calls,failed_calls,total_calls);
	pthread_mutex_unlock(&mutex);
	
}
void call_failed(int handle,int progress)
{
	pthread_mutex_lock(&mutex);
	failed_calls++;
	total_calls++;
	myprintf("chan[%d]: PASS (%d) FAIL (%d) TOTAL(%d) [failed in progress (%d)]\n",handle,passed_calls,failed_calls,total_calls,progress);
	pthread_mutex_unlock(&mutex);
}
