/*****************************************************************************
* aft_api.c	AFT T1/E1: HDLC API Sample Code
*
* Author(s):	Nenad Corbic <ncorbic@sangoma.com>
*
* Copyright:	(c) 2003-2004 Sangoma Technologies Inc.
*
*		This program 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.
* ============================================================================
*/


#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_wanpipe.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <signal.h>
#include <linux/wanpipe_defines.h>
#include <linux/wanpipe_cfg.h>
#include <linux/wanpipe.h>
#include <libsangoma.h>
#include "lib_api.h"

#define MAX_TX_DATA     5000	/* Size of tx data */  
#define MAX_FRAMES 	5000     /* Number of frames to transmit */  

#define MAX_RX_DATA	5000

unsigned short Rx_lgth;
int gexit=0;
unsigned char Rx_data[MAX_RX_DATA];
unsigned char Tx_data[MAX_TX_DATA + sizeof(wp_tdm_api_rx_hdr_t)];

/* Prototypes */
int MakeConnection(void);
void handle_span_chan( void);
void sig_end(int sigid);

int dev_fd;  
FILE *tx_fd=NULL,*rx_fd=NULL;	
wanpipe_tdm_api_t tdm_api; 

static int	event_cnt = 0;
unsigned char	event_type = 0x00, tone_id = 0x00;

static int aft_tdm_api_event_ctrl(wanpipe_tdm_api_t *tdm_api, int mode);

static int aft_tdm_api_ring_detect_event(int fd, unsigned char state)
{
	int err=0;
	printf("%04d --> Ring Event: %s!\n",
			event_cnt++,
			WP_TDMAPI_EVENT_RING_TRIP_DECODE(state));


	if (state == WP_TDMAPI_EVENT_RING_PRESENT) {	

		sleep(1);
		sangoma_tdm_disable_ring_events(dev_fd, &tdm_api);
		sleep(1);	
#if 0 
#if 1
		sangoma_tdm_txsig_start(dev_fd, &tdm_api);
		usleep(500);
#else
		sangoma_tdm_txsig_kewl(dev_fd, &tdm_api);
		usleep(500);
#endif
#endif
		sangoma_tdm_txsig_offhook(dev_fd, &tdm_api);
		usleep(500);
		printf("%04d --> Tx OFF HOOK (%i)!\n", event_cnt,err);

	} else {
		//sangoma_tdm_txsig_onhook(dev_fd, &tdm_api);
		//printf("%04d --> Tx ON HOOK!\n", event_cnt);
	}

	return 0;
}
static int aft_tdm_api_ring_trip_detect_event(int fd, unsigned char state)
{
	printf("%04d --> Ring Trip Event: %s!\n",
			event_cnt++,
			WP_TDMAPI_EVENT_RING_TRIP_DECODE(state));
	return 0;
}
static int
aft_tdm_api_dtmf_event(int fd, unsigned char digit, unsigned char type, unsigned char port)
{
         printf("%04d --> DTMV Event: %c (%s:%s)!\n",
			event_cnt++,
			digit, 
			(port == WAN_EC_CHANNEL_PORT_ROUT)?"ROUT":"SOUT",
			(type == WAN_EC_TONE_PRESENT)?"PRESET":"STOP");
        return 0;
}
static int aft_tdm_api_rxhook_event(int fd, unsigned char state)
{
	printf("%04d --> RXHOOK Event: %s!\n",
			event_cnt++,
			WP_TDMAPI_EVENT_RXHOOK_DECODE(state));

#if 0
	if (state == WP_TDMAPI_EVENT_RXHOOK_ON) {
		printf("%04d --> Tx ON HOOK!\n", event_cnt);
		sangoma_tdm_txsig_onhook(dev_fd, &tdm_api);
	} 
#endif

	return 0;
}



static int aft_tdm_api_event_ctrl(wanpipe_tdm_api_t *tdm_api, int mode)
{

	switch(event_type){
	case WP_TDMAPI_EVENT_RING:
		if (mode){
			sangoma_tdm_enable_ring_events(dev_fd, tdm_api);
		}else{
			sangoma_tdm_disable_ring_events(dev_fd, tdm_api);
		}
		break;
	case WP_TDMAPI_EVENT_RING_DETECT:
		if (mode){
			tdm_api->wp_tdm_event.wp_ring_detect_event = &aft_tdm_api_ring_detect_event;
			sangoma_tdm_enable_ring_detect_events(dev_fd, tdm_api);
		}else{
			sangoma_tdm_disable_ring_detect_events(dev_fd, tdm_api);
		}
		break;
	case WP_TDMAPI_EVENT_RING_TRIP_DETECT:
		if (mode){
			tdm_api->wp_tdm_event.wp_ring_trip_detect_event =
						&aft_tdm_api_ring_trip_detect_event;
			sangoma_tdm_enable_ring_trip_detect_events(dev_fd, tdm_api);
		}else{
			sangoma_tdm_disable_ring_trip_detect_events(dev_fd, tdm_api);
		}
		break;
	case WP_TDMAPI_EVENT_DTMF:
		if (mode){
			tdm_api->wp_tdm_event.wp_dtmf_event = &aft_tdm_api_dtmf_event;
			sangoma_tdm_enable_ring_trip_detect_events(dev_fd, tdm_api);
		}else{
			sangoma_tdm_disable_ring_trip_detect_events(dev_fd, tdm_api);
		}
		break;
	case WP_TDMAPI_EVENT_RM_DTMF:
		if (mode){
			tdm_api->wp_tdm_event.wp_dtmf_event = &aft_tdm_api_dtmf_event;
			sangoma_tdm_enable_rm_dtmf_events(dev_fd, tdm_api);
		}else{
			sangoma_tdm_disable_rm_dtmf_events(dev_fd, tdm_api);
		}
		break;
	case WP_TDMAPI_EVENT_RXHOOK:
		if (mode){
			tdm_api->wp_tdm_event.wp_rxhook_event = &aft_tdm_api_rxhook_event;
			sangoma_tdm_enable_rxhook_events(dev_fd, tdm_api);
		}else{
			sangoma_tdm_disable_rxhook_events(dev_fd, tdm_api);
		}
		break;
	}
	return 0;
}
/***************************************************
* HANDLE SOCKET 
*
*   o Read a socket 
*   o Cast data received to api_rx_element_t data type 
*   o The received packet contains 16 bytes header 
*
*	------------------------------------------
*      |  16 bytes      |        X bytes        ...
*	------------------------------------------
* 	   Header              Data
*
* o Data structures:
* ------------------
* typedef struct {
* 	union {
* 		struct {
* 			unsigned char	_event_type;
* 			unsigned char	_rbs_rx_bits;
* 			unsigned int	_time_stamp;
* 		}wp_event;
* 		struct {
* 			unsigned char	_rbs_rx_bits;
* 			unsigned int	_time_stamp;
* 		}wp_rx;
* 		unsigned char	reserved[16];
* 	}wp_rx_hdr_u;
* #define wp_api_event_type		wp_rx_hdr_u.wp_event._event_type
* #define wp_api_event_rbs_rx_bits 	wp_rx_hdr_u.wp_event._rbs_rx_bits
* #define wp_api_event_time_stamp 	wp_rx_hdr_u.wp_event._time_stamp
* } wp_tdm_api_rx_hdr_t;
* 
* typedef struct {
*         wp_tdm_api_rx_hdr_t	hdr;
*         unsigned char  		data[1];
* } wp_tdm_api_rx_element_t;
* 
* typedef struct {
* 	union {
* 		struct {
* 			unsigned char	_rbs_rx_bits;
* 			unsigned int	_time_stamp;
* 		}wp_tx;
* 		unsigned char	reserved[16];
* 	}wp_tx_hdr_u;
* #define wp_api_time_stamp 	wp_tx_hdr_u.wp_tx._time_stamp
* } wp_tdm_api_tx_hdr_t;
* 
* typedef struct {
*         wp_tdm_api_tx_hdr_t	hdr;
*         unsigned char  		data[1];
* } wp_tdm_api_tx_element_t;
*                     
* #define WPTDM_A_BIT 0x08
* #define WPTDM_B_BIT 0x04
* #define WPTDM_C_BIT 0x02
* #define WPTDM_D_BIT 0x01
*
*/

void handle_span_chan(void) 
{
	unsigned int Rx_count,Tx_count,Tx_length;
	wp_tdm_api_rx_element_t* api_rx_el;
	wp_tdm_api_tx_element_t * api_tx_el;
	fd_set 	ready,write,oob;
	int err,i;
	
#if 0
	int rlen;
	int stream_sync=0;
#endif
	
	Rx_count = 0;
	Tx_count = 0;

	if (tdm_api.wp_tdm_cmd.hdlc) {
                Tx_length = tx_size;
	} else {
		Tx_length = tdm_api.wp_tdm_cmd.usr_mtu_mru;
	}
	
	printf("\n\nSocket Handler: Rx=%d Tx=%i TxCnt=%i TxLen=%i TxDelay=%i\n",
			read_enable,write_enable,tx_cnt,tx_size,tx_delay);	

	/* Initialize the Tx Data buffer */
	memset(&Tx_data[0],0,MAX_TX_DATA + sizeof(wp_tdm_api_rx_hdr_t));

	/* Cast the Tx data packet with the tx element
	 * structure.  We must insert a 16 byte
	 * driver header, which driver will remove 
	 * before passing packet out the physical port */
	api_tx_el = (wp_tdm_api_tx_element_t*)&Tx_data[0];
	

	/* Create a Tx packet based on user info, or
	 * by deafult incrementing number starting from 0 */
	for (i=0;i<Tx_length;i++){
		if (tx_data == -1){
			api_tx_el->data[i] = (unsigned char)i;
		}else{
#if 0
			api_tx_el->data[i] = (unsigned char)tx_data+(i%4);
#else
			api_tx_el->data[i] = (unsigned char)tx_data;
#endif
		}
	}

//	sangoma_tdm_txsig_onhook(dev_fd, &tdm_api);

	event_type=WP_TDMAPI_EVENT_RING_DETECT;
	aft_tdm_api_event_ctrl(&tdm_api, 1);

	event_type=WP_TDMAPI_EVENT_RING_TRIP_DETECT;
	aft_tdm_api_event_ctrl(&tdm_api, 1);

	event_type=WP_TDMAPI_EVENT_RXHOOK;
	aft_tdm_api_event_ctrl(&tdm_api, 1);

	event_type=WP_TDMAPI_EVENT_RING;
	aft_tdm_api_event_ctrl(&tdm_api, 1);		
			


	/* Main Rx Tx OOB routine */
	for(;;) {	

		if (gexit)
			break;

		/* Initialize all select() descriptors */
		FD_ZERO(&ready);
		FD_ZERO(&write);
		FD_ZERO(&oob);
		FD_SET(dev_fd,&oob);
		FD_SET(dev_fd,&ready);

		if (write_enable){
		     FD_SET(dev_fd,&write);
		}

		/* Select will block, until:
		 * 	1: OOB event, link level change
		 * 	2: Rx data available
		 * 	3: Interface able to Tx */
		
  		if(select(dev_fd + 1,&ready, &write, &oob, NULL)){

			if (gexit)
				break;

			fflush(stdout);	
		   	if (FD_ISSET(dev_fd,&oob)){
		
				/* An OOB event is pending, usually indicating
				 * a link level change */
				
				err=sangoma_tdm_read_event(dev_fd,&tdm_api);
				
			}
		  
			
          		if (FD_ISSET(dev_fd,&ready)){

				/* An Rx packet is pending
				 * 	1: Read the rx packet into the Rx_data
				 * 	   buffer. Confirm len > 0
				 *
				 * 	2: Cast Rx_data to the api_rx_element.
				 * 	   Thus, removing a 16 byte header
				 * 	   attached by the driver.
				 *
				 * 	3. Check error_flag:
				 * 		CRC,Abort..etc 
				 */

				memset(Rx_data, 0, sizeof(Rx_data));

				err = sangoma_readmsg_tdm(dev_fd,
							Rx_data, 
							sizeof(wp_tdm_api_rx_hdr_t),
							&Rx_data[sizeof(wp_tdm_api_rx_hdr_t)],
							MAX_RX_DATA, 0);   


				if (!read_enable){
					goto bitstrm_skip_read;
				}

				/* err indicates bytes received */
				if(err <= 0) {
					printf("\nError receiving data\n");
					break;
				}

				api_rx_el = (wp_tdm_api_rx_element_t*)&Rx_data[0];

				/* Check the packet length */
				Rx_lgth = err;
				if(Rx_lgth<=0) {
					printf("\nShort frame received (%d)\n",
						Rx_lgth);
					return;
				}

#if 0
				if (api_rx_el->data[0] == tx_data && api_rx_el->data[1] == (tx_data+1)){
					if (!stream_sync){
						printf("GOT SYNC %x\n",api_rx_el->data[0]);
					}
					stream_sync=1;
				}else{
					if (stream_sync){
						printf("OUT OF SYNC: %x\n",api_rx_el->data[0]);
					}
				}
#endif					

				++Rx_count;

				if (verbose){
#if 0
					printf("Received %i Length = %i\n", 
							Rx_count,Rx_lgth);

					printf("Data: ");
					for(i=0;i<Rx_lgth; i ++) {
						printf("0x%02X ", api_rx_el->data[i]);
					}
					printf("\n");
#endif
				}else{
					//putchar('R');
				}


#if 0
				switch(api_rx_el->hdr.wp_api_event_type){
				case WP_TDM_EVENT_DTMF:
					printf("DTMV Event: %c (%s:%s)!\n",
						api_rx_el->hdr.wp_api_event_dtmf_digit,
						(api_rx_el->hdr.wp_api_event_dtmf_type&WP_TDM_EVENT_DTMF_ROUT)?"ROUT":"SOUT",
						(api_rx_el->hdr.wp_api_event_dtmf_type&WP_TDM_EVENT_DTMF_PRESET)?"PRESET":"STOP");		
					break;
				case WP_TDM_EVENT_RXHOOK:
					printf("RXHOOK Event: %s!\n",
						(api_rx_el->hdr.wp_api_event_rxhook_state&WP_TDM_EVENT_RXHOOK_OFF)?"OFF-HOOK":"ON-HOOK");
					break;
				case WP_TDM_EVENT_RING:
					printf("RING Event: %s!\n",
						(api_rx_el->hdr.wp_api_event_ring_state&WP_TDM_EVENT_RING_PRESENT)?"PRESENT":"STOP");
					break;
				}
#endif

				if (rx_cnt > 0  && Rx_count >= rx_cnt){
					break;
				}
bitstrm_skip_read:
;
		   	}
		
		   	if (FD_ISSET(dev_fd,&write)){


				err = sangoma_writemsg_tdm(dev_fd,
						       Tx_data,16, 
						       &Tx_data[16], Tx_length, 
						       0);
				if (err <= 0){
					if (errno == EBUSY){
						if (verbose){
							printf("Sock busy try again!\n");
						}
						/* Socket busy try sending again !*/
					}else{
						printf("Faild to send %i \n",errno);
						perror("Send: ");
						break;
					}
				}else{

					++Tx_count;
					
					if (verbose){
						//printf("Packet sent: Sent %i : %i\n",
						//	err,Tx_count);
					}else{
						//putchar('T');
					}
				}
		   	}

			if (tx_delay){
				usleep(tx_delay);
			}

			if (tx_cnt && tx_size && Tx_count >= tx_cnt && !(files_used & TX_FILE_USED)){
			
				write_enable=0;
				if (rx_cnt > 0){
					/* Dont break let rx finish */
				}else{
					break;
				}
			}
		}
	}

	printf("Stopping Device\n");
    	sangoma_tdm_disable_ring_events(dev_fd, &tdm_api);
	if (tx_fd){
		fclose(tx_fd);
	}
	if (rx_fd){
		fclose(rx_fd);
	}
	close (dev_fd);
	return;

} 


static int event_ctrl(int argc, char* argv[])
{
	int i=0;
	
	for (i = 0; i < argc; i++){
		if (!strcmp(argv[i],"-tone")){

			if (i+1 > argc-1){
				printf("ERROR: Invalid Interface Name!\n");
				return WAN_FALSE;
			}
			if(!isdigit(argv[i+1][0])){
				printf("ERROR: Invalid tone id!\n");
				return WAN_FALSE;			
			}
			event_type = WP_TDMAPI_EVENT_TONE;			
			tone_id = atoi(argv[i+1]);
		}
		if (!strcmp(argv[i],"-ring")){
			event_type = WP_TDMAPI_EVENT_RING;
		}
		if (!strcmp(argv[i],"-ringdetect")){
			event_type = WP_TDMAPI_EVENT_RING_DETECT;
		}
		if (!strcmp(argv[i],"-ringtrip")){
			event_type = WP_TDMAPI_EVENT_RING_TRIP_DETECT;
		}
		if (!strcmp(argv[i],"-rxhook")){
			event_type = WP_TDMAPI_EVENT_RXHOOK;
		}
		if (!strcmp(argv[i],"-dtmf")){
			event_type = WP_TDMAPI_EVENT_DTMF;
		}
		if (!strcmp(argv[i],"-rm_dtmf")){
			event_type = WP_TDMAPI_EVENT_DTMF;
		}
	}
	return 0;
}

/***************************************************************
 * Main:
 *
 *    o Make a socket connection to the driver.
 *    o Call handle_span_chan() to read the socket 
 *
 **************************************************************/


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

	proceed=init_args(argc,argv);
	if (proceed != WAN_TRUE){
		usage(argv[0]);
		return -1;
	}
	if (event_ctrl(argc,argv)){
		usage(argv[0]);
		return -1;
	}

	signal(SIGINT,&sig_end);
	signal(SIGTERM,&sig_end);
	memset(&tdm_api,0,sizeof(tdm_api));
	
	dev_fd =-1;
	  
	dev_fd = sangoma_open_tdmapi_span_chan(atoi(card_name),atoi(if_name));
	if( dev_fd < 0){
		printf("Failed to open span chan (%s:%s:%d:%d)\n",
					card_name, if_name,
					atoi(card_name),atoi(if_name));
		exit (1);
	}
	printf("HANDLING SPAN %i CHAN %i FD=%i\n",
		atoi(card_name),atoi(if_name),dev_fd);
		
	sangoma_tdm_set_codec(dev_fd,&tdm_api,WP_NONE);
	sangoma_get_full_cfg(dev_fd, &tdm_api);
		
	handle_span_chan();
	close(dev_fd);
	return 0;

};


void sig_end(int sigid)
{
	
	printf("!!! Got Signal %i !!!\n",sigid);
	gexit=1;
	
}



