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

	FILE....: voicecallgen.cpp
	TYPE....: C++ Console program
	AUTHOR..: Mark Mickan
	DATE....: July 2007

	This program is based on Ben's callgen.cpp, but instead
       	of sending DTMF it plays audio files in both directions.
	Calls are never terminated, so it can be used for testing
       	the logger in CAS mode.

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

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

         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	20*1000

void *call_gen_thread(void *pv);
void *call_rec_thread(void *pv);
int arg_exists(int argc, char *argv[], char *arg);

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);

	// 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(number,"21");
	sprintf(cid,"40711111%02d",cinfo->handle+1);
	void	*call_timer;

	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);
							//myprintf("chan[%d]: Dialing DTMF's (%s)\n",cinfo->handle,cid);
							vpb_play_file_async(cinfo->handle,"numbers.wav",69);
							//myprintf("chan[%d]: Dialed DTMF's (%s)\n",cinfo->handle,cid);
							newstate = CALL_ANS;
							break;
						case VPB_DROP:
							myprintf("chan[%d]: The caller hung up in WT_ANS \n",cinfo->handle);
							vpb_timer_stop(call_timer);
							newstate = CALL_IDLE;
							break;
						case VPB_TIMEREXP:
							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_PLAYEND:
							break;
						case VPB_DROP:
							myprintf("chan[%d]: The caller hung up in ANS\n",cinfo->handle);
							vpb_timer_stop(call_timer);
							newstate = CALL_IDLE;
							break;
						case VPB_TIMEREXP:
							vpb_timer_stop(call_timer);
							vpb_timer_start(call_timer);
							vpb_play_file_async(cinfo->handle,"numbers.wav",69);
							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:
							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);
		}
	}
	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;
	void	*call_timer;

	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){
						vpb_timer_start(call_timer);
						vpb_isdn_get_cinfo(e.handle,call_info);
						myprintf("chan[%d]: New call! [%d]\n",e.handle,call_info);
						vpb_isdn_proceeding(e.handle);
						sleep(1);
						vpb_sethook_sync(e.handle,VPB_OFFHOOK);
						vpb_play_file_async(cinfo->handle,"numbers.wav",69);
						newstate = CALL_ANS;
						myprintf("chan[%d]: Answered call \n",e.handle);
					}
					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;
					}
					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){
						vpb_timer_stop(call_timer);
						vpb_timer_start(call_timer);
						vpb_play_file_async(cinfo->handle,"numbers.wav",69);
					}
					else if (e.type == VPB_DIALEND){
						/* Do nothing! */
					}
					else if (e.type == VPB_TONEDETECT){
						/* Do nothing! */
					}
					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;
				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);
}
