/*
 * FILE:    init_session.c 
 * PROGRAM: Rat
 * AUTHORS: Vicky Hardman + Isidor Kouvelas + Colin Perkins
 * 
 * $Revision: 1.1.1.1 $
 * $Date: 1998/08/15 19:16:46 $
 * 
 * Copyright (c) 1995,1996,1997 University College London
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, is permitted, for non-commercial use only, provided
 * that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Computer Science
 *      Department at University College London
 * 4. Neither the name of the University nor of the Department may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 * Use of this software for commercial purposes is explicitly forbidden
 * unless prior written permission is obtained from the authors.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "bat_include.h"
#include "version.h"

int thread_pri;

void
init_session(session_struct *sp)
{
	struct hostent *addr;
	u_long          netaddr;
	char            hostname[MAXHOSTNAMELEN + 1];
	int		i;

	memset(sp, 0, sizeof(session_struct));
	thread_pri = 0;	/* Don't nobble priority of this thread - unnecessary! */

	sp->ui				=0;
	sp->mode         		= AUDIO_TOOL;	
	sp->cur_time			= 0;		/* Should be random */
	sp->net_maddress		= 0;		/* Same as above, can be used in a sendto */
	sp->our_address			= 0;		/* our unicast address */
	sp->rtp_port			= 0;
	sp->rtcp_port			= 1;
	sp->ttl				= 16;
	sp->rtp_fd			= -1;
	sp->rtcp_fd			= -1;
        sp->filter_loopback             = TRUE;
        sp->encodings[0]		= DVI;		/* user chosen encoding for primary */
	sp->num_encodings		= 1;		/* Number of encodings in packet */
	sp->sending_audio		= FALSE;
	sp->playing_audio		= TRUE;
	sp->lecture			= TRUE;
	sp->units_per_pckt		= 2;
	sp->transmit_audit_required	= FALSE;
	sp->receive_audit_required	= FALSE;
	sp->voice_switching		= FULL_DUPLEX;	/* NETMUTESMIKE etc. */
	sp->redundancy_pt		= 121;
	sp->l16_pt    			= 122;
	sp->detect_silence		= TRUE;
        sp->ui_on                       = TRUE;
	sp->ui_response			= 15;
	sp->loop_delay			= -1;		/* Real initialisation is in init_audio.c */
	sp->loop_estimate		= -1;		/* Real initialisation is in init_audio.c */
	sp->last_zero              	= FALSE;
	sp->repair			= REPAIR_REPEAT;/* Packet repetition */
	sp->meter			= TRUE;		/* Powermeter operation */
        sp->drop                        = 0.0;
	sp->in_file 			= NULL;
	sp->out_file  			= NULL;
	sp->audio_fd               	= -1;
	sp->double_speed               	= FALSE;
        sp->have_device                 = 0;
        sp->keep_device                 = 0;
	sp->sd_info                	= NULL;
        sp->flake_go                    = 0;
	sp->mix_count   		= 0;
	sp->rtp_seq			= rand()&0xffff;/* Let's hope srand() has been called, and that rand() is really random [csp] */
	sp->adpcm_state.valprev    	= 0;		/* Not sure these three are sensible... any ideas? [csp] */
	sp->adpcm_state.index      	= 0;
	sp->adpcm_state.pad        	= 0;
	sp->speakers_active 		= NULL;
	sp->lbl_cb_base_socket		= -1;		/* LBL conference bus support */
	sp->lbl_cb_channel_socket	= -1;
	sp->lbl_cb_channel		= -1;
        sp->lbl_cb_priority             = 100;
        sp->bc.active                   = TRUE;         /* bias control ON */
        sp->bc.bias                     = 0;
        sp->bc.ccnt                     = 0;
	/* Initialise the RTCP database to null values, further initialisation is */
	/* done in rtp.c::rtp_init()                                              */
	sp->db.myssrc		= 0;
	sp->db.old_ssrc		= 0;
	sp->db.pkt_count	= 0;
	sp->db.byte_count	= 0;
	sp->db.pckts_received	= 0;
	sp->db.misordered	= 0;
	sp->db.duplicates	= 0;
	sp->db.TOGed		= 0;
	sp->db.last_rpt		= 0;
	sp->db.sending		= 0;
	sp->db.map_rtp_time	= 0;
	sp->db.map_ntp_time	= 0;
	sp->db.sec_count	= 0;
	sp->db.dummy_count	= 0;
	sp->db.normal_count	= 0;
	sp->db.my_dbe		= NULL;
	sp->db.ssrc_db		= NULL;
	sp->db.members		= 1;
	sp->db.senders		= 0;
	sp->db.rtcp_bw		= 417.5;		/* 5% of 8350 bytes/sec (8khz, pcmu) session bandwidth */
	sp->db.avg_size		= 0;
	sp->db.initial_rtcp	= TRUE;
	sp->db.sdes_pri_count   = 0;
	sp->db.sdes_sec_count   = 0;
	sp->db.sdes_ter_count   = 0;
	sp->db.report_interval 	= rtcp_interval(sp->db.members, sp->db.senders, sp->db.rtcp_bw, sp->db.sending, 
					        128, &(sp->db.avg_size), sp->db.initial_rtcp);
	for (i=0; i<RTP_NUM_SDES; i++) {
		sp->db.sdes[i] = NULL;
	}

	if (gethostname(hostname, MAXHOSTNAMELEN + 1) != 0) {
		perror("Cannot get hostname!");
		abort();
	}
	addr = gethostbyname(hostname);
	memcpy(&netaddr, addr->h_addr, 4);
	sp->ipaddr = ntohl(netaddr);

	gettimeofday(&(sp->device_time), NULL); 

	strcpy(sp->maddress,    "127.0.0.2");	/* Yeuch! This value should never be used! */
	strcpy(sp->asc_address, "127.0.0.3");	/* Yeuch! This value should never be used! */
}

void 
parse_options_common(int argc, char *argv[], session_struct *sp[], int sp_size)
{
	/* Parse command-line options common to all the modes of operation.     */
	/* Variables: i scans through the options, s scans through the sessions */
	int i, s;

	for (i = 1; i < argc; i++) {
		for (s = 0; s < sp_size; s++) {
			if ((strcmp(argv[i], "-K") == 0) && (argc > i+1)) {
				argv[i] = "-crypt";
				i++;
			}
			if ((strcmp(argv[i], "-C") == 0) && (argc > i+1)) {
				argv[i] = "-name";
				i++;
			}
			if ((strcmp(argv[i], "-t") == 0) && (argc > i+1)) {
				sp[s]->ttl = atoi(argv[i + 1]);
				if (sp[s]->ttl > 255) {
					usage();
				}
				i++;
			}
			if ((strcmp(argv[i], "-p") == 0) && (argc > i+1)) {
				if ((thread_pri = atoi(argv[i + 1])) > 3) {
					usage();
				}
				i++;
			}
			if ((strcmp(argv[i], "-drop") == 0) && (argc > i+1)) {
			if ((strcmp(argv[i], "-smallui")==0)) {
				sp[s]->ui = 1;	/* Small */
				argv[i] = "";
				i++;
			}
                        	sp[s]->drop = atof(argv[++i]);
                        	if (sp[s]->drop > 1.0) {
                                	sp[s]->drop = sp[s]->drop/100;
                        	}
				i++;
                	}
                	if (strcmp(argv[i], "-seed") == 0) {
                        	srand48(atoi(argv[++i]));
                	}
			if ((strcmp(argv[i], "-pt") == 0) && (argc > i+1)) {
                		/* Dynamic payload type mapping. Format: "-pt pt/codec/clock/channels" */
				/* pt/codec must be specified. clock and channels are optional.        */
				/* At present we only support "-pt .../redundancy"                     */
				sp[s]->redundancy_pt = atoi(strtok(argv[i + 1], "/"));
				if ((sp[s]->redundancy_pt > 127) || (sp[s]->redundancy_pt < 96)) {
					printf("Dynamic payload types must be in the range 96-127. So there.\n");
					usage();
				}
				if (!strncmp(strtok(NULL, "/"), "redundancy", 15)) {
					if (strtok(NULL, "/") != NULL) {
						printf("Hmmm.... Don't understand that -pt option\n");
						usage();
					}
				} else {
					printf("Unsupported -pt option.\n");
					usage();
				}
				i++;
			}
			if ((strcmp(argv[i], "-silence") == 0) && (argc > i+1)) {
                                if (strcmp(argv[i+1], "on") == 0) {
                                        sp[s]->detect_silence = TRUE;
                                        i++;
                                } else if (strcmp(argv[i+1], "off") == 0) {
                                        sp[s]->detect_silence = FALSE;
                                        i++;
                                } else {
                                        printf("Unrecognized -silence option.\n");

                                }
			}
                        if ((strcmp(argv[i], "-silence_params") == 0) && (argc > i+1)) {
				set_silence_params(argv[i + 1]);
                                i++;
                        }
                        if ((strcmp(argv[i], "-repair") == 0) && (argc > i+1)) {
                                if (strncmp(argv[i+1], "none", 4)==0) {
                                        sp[s]->repair = REPAIR_NONE;
                                        i++;
                                } else if (strncmp(argv[i+1], "repeat", 6)==0) {
                                        sp[s]->repair = REPAIR_REPEAT;
                                        i++;
                                } else {
                                        printf("Unsupported -repair option: %s.\n", argv[i++]);
                                }
                        }
			if ((strcmp(argv[i], "-f") == 0) && (argc > i+1)) {
				int   j = 0;
				char *p = strtok(argv[i+1], "/");
				while ((p != NULL) && (j < 5)) {
    					if        (strncmp(p, "pcm", 4) == 0) {
						sp[s]->encodings[j]  = PCM;
						sp[s]->num_encodings = ++j;
     					} else if (strncmp(p, "dvi", 4) == 0) {
						sp[s]->encodings[j]  = DVI;
						sp[s]->num_encodings = ++j;
     					} else if (strncmp(p, "lpc", 4) == 0) {
						sp[s]->encodings[j]  = LPC;
						sp[s]->num_encodings = ++j;
     					} else if (strncmp(p, "gsm", 4) == 0) {
						sp[s]->encodings[j]  = GSM;
						sp[s]->num_encodings = ++j;
     					} else if (strncmp(p, "l16", 4) == 0) {
						sp[s]->encodings[j]  = L16;
						sp[s]->num_encodings = ++j;
     					} else {
						usage();
					}
					p = strtok(NULL, "/");
				}
				i++;
			}
			if (strcmp(argv[i], "-allow_loopback") == 0) {
				sp[s]->filter_loopback = FALSE;
			}
		}
	}
}

void 
parse_options_audio_tool(int argc, char *argv[], session_struct *sp)
{
	/* Parse command-line options specific to the audio tool */
	int   i;
	char *p;

	for (i = 1; i < argc; i++) {
		if (strcmp(argv[i], "-no_ui") == 0) {
                        sp->ui_on = FALSE;
                }
		if ((strcmp(argv[i], "-lbl_channel") == 0) && (argc > i+1)) {
			lbl_cb_init_channel(sp, atoi(argv[i + 1]));
			i++;
		}
		if ((strcmp(argv[i], "-lbl_priority") == 0) && (argc > i+1)) {
			sp->lbl_cb_priority = atoi(argv[i + 1]);
			i++;
		}
	}

	p = strtok(argv[argc - 1], "/");
	strcpy(sp->asc_address, p);
	strcpy(sp->maddress, p);
	if ((p = strtok(NULL, "/")) != NULL) {
		sp->rtp_port = atoi(p);
		sp->rtp_port &= ~1;
		sp->rtcp_port = sp->rtp_port + 1;
	}
}

void 
parse_options_flakeaway(int argc, char *argv[], session_struct *sp)
{
	/* Parse command-line options specific to the flakeaway */
	if ((sp->in_file = fopen(argv[argc-2],"r")) == NULL) {
		fprintf(stderr, "Could not open %s\n", argv[argc-2]);
		exit(1);
	}
	if ((sp->out_file = fopen(argv[argc-1],"w")) == NULL) {
		fprintf(stderr, "Could not open %s\n", argv[argc-1]);
		exit(1);
	}
	sprintf(sp->asc_address, "127.0.0.1");
	sprintf(sp->maddress,"127.0.0.1");
	sp->rtp_port           = 45450;
	sp->rtcp_port          = 45451;
        sp->db.initial_rtcp    = FALSE;
        sp->db.senders         = 1;
        sp->db.report_interval = 160;
        sp->db.last_rpt        = -2*sp->db.report_interval;
	sp->ui_on              = FALSE;
	sp->playing_audio      = TRUE;
                /* these are to stop audio being discarded during initial buffer
                   realignment, and to stop the app at the right time.
                   There are undoubtedly better ways to do this (as always)
                   */
        sp->flake_go      = 160;
                /* this is to compensate for  buffering time in transmitter and receiver */
        sp->flake_os      = 2000;  
}

void 
parse_options_transcoder(int argc, char *argv[], session_struct *sp[])
{
	/* Parse command-line options specific to the transcoder */
	int   i, j;
	char *p;

	if (argc < 4) {
		usage();
	}

	for (i=0; i<2; i++) {
		/* General setup... */
		sp[i]->ui_on = FALSE;
		sp[i]->playing_audio = TRUE;
		/* addr */
		p = strtok(argv[argc-i-1], "/");
		strcpy(sp[i]->asc_address, p);
		/* port */
		if ((p = strtok(NULL, "/")) != NULL) {
			sp[i]->rtp_port  = atoi(p);
			sp[i]->rtp_port &= ~1;
			sp[i]->rtcp_port = sp[i]->rtp_port + 1;
		} else {
			usage();
		}
		/* ttl */
		if ((p = strtok(NULL, "/")) != NULL) {
			sp[i]->ttl = atoi(p);
		} else {
			continue;
		}
		/* encoding */
		j = 0;
		while ((p = strtok(NULL, "/")) != NULL) {
    			if        (strncmp(p, "pcm", 4) == 0) {
				sp[i]->encodings[j]  = PCM;
				sp[i]->num_encodings = ++j;
     			} else if (strncmp(p, "dvi", 4) == 0) {
				sp[i]->encodings[j]  = DVI;
				sp[i]->num_encodings = ++j;
     			} else if (strncmp(p, "lpc", 4) == 0) {
				sp[i]->encodings[j]  = LPC;
				sp[i]->num_encodings = ++j;
     			} else if (strncmp(p, "gsm", 4) == 0) {
				sp[i]->encodings[j]  = GSM;
				sp[i]->num_encodings = ++j;
     			} else if (strncmp(p, "l16", 4) == 0) {
				sp[i]->encodings[j]  = L16;
				sp[i]->num_encodings = ++j;
     			} else {
				usage();
			}
		}
	}
}

int
parse_options(int argc, char *argv[], session_struct *sp[])
{
	int	i, num_sessions = 0;

	if (argc < 2) {
		usage();
	}
	/* Set the mode of operation, and number of valid sessions, based on the first command line option. */
	if (strcmp(argv[1], "-version") == 0) {
		printf("%s\n", RAT_VERSION);
		exit(0);
	} else if (strcmp(argv[1], "-F") == 0) {
		sp[0]->mode = FLAKEAWAY;
		num_sessions= 1;
	} else if (strcmp(argv[1], "-T") == 0) {
		sp[0]->mode = TRANSCODER;
		sp[1]->mode = TRANSCODER;
		num_sessions= 2;
	} else {
		sp[0]->mode = AUDIO_TOOL;
		num_sessions= 1;
	}
	parse_options_common(argc, argv, sp, num_sessions);
	switch (sp[0]->mode) {
		case AUDIO_TOOL: parse_options_audio_tool(argc, argv, sp[0]);
				 break;
		case FLAKEAWAY : parse_options_flakeaway(argc, argv, sp[0]);
				 break;
		case TRANSCODER: parse_options_transcoder(argc, argv, sp);
				 break;
		default        : abort();
	}
	for (i=0; i<num_sessions; i++) {
		if (sp[i]->rtp_port == 0) {
			printf("Address and port required!\n");
			usage();
		}
	}
	return num_sessions;
}

