/*
 * Phonecore: A client phone for the Asterisk PBX
 *
 * Copyright (C) 2000, Linux Support Services, Inc.
 *
 * Written by Mark Spencer
 *
 * Linux/UNIX version distributed under the terms of
 * the GNU General Public License
 *
 * phonecore.c: Core telephony thread
 *
 */
#include <stdio.h>
#include <sys/select.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include "phonecore.h"

#ifdef SNOM_HACK
	#include "snom_memset.h"
#endif
 
static int portno;

static void print_prompt(void)
{
	printf("pctest> ");
	fflush(stdout);
}

static int handle_pcevent(void)
{
	pc_event *e;
	e = pc_get_event();
	if (e) {
		pc_dump_event(e);
		switch(e->event) {
		case PC_EVENT_REGREP:
			switch (e->e.regreply.status) {
			case PC_REG_SUCCESS:
				printf("Registered to '%s' who sees us as '%s:%d'\n", e->e.regreply.them, e->e.regreply.ourip, e->e.regreply.ourport);
				if (strlen(e->e.regreply.callerid)) {
					printf("%s says we are '%s'\n", e->e.regreply.them, e->e.regreply.callerid);
				}
				break;
			default:
				fprintf(stderr, "Unknown reg reply status: %d\n", e->e.regreply.status);
			}
			break;
		case PC_EVENT_ADDR:
			printf("Remote peer is '%s' port %d\n", inet_ntoa(e->e.addr.addr.sin_addr), ntohs(e->e.addr.addr.sin_port));
			break;
		case PC_EVENT_CONNECT:
			printf("Connect request from '%s' (%s) to '%s'\n", e->e.connect.callerid, inet_ntoa(e->e.connect.addr.sin_addr), e->e.connect.dest);
			pc_audio_ring();
			break;
		case PC_EVENT_HANGUP:
			printf("Hangup on call %d (%s)\n", e->callno, e->e.hangup.why);
			break;
		case PC_EVENT_LAGREP:
			printf("Lag/Jitter is %d/%d on call %d\n", e->e.lag.lag, e->e.lag.jitter, e->callno);
			break;
		case PC_EVENT_ANSWER:
			printf("Got a PC_EVENT_ANSWER\n");
			break;
		case PC_EVENT_RINGA:
			pc_audio_ringing();
			break;
		default:
			fprintf(stderr, "Dunno what to do with phonecore event '%d'\n", e->event);
		}
	}
	return 0;
}

#define MAX_ARGS 32

static int handle_command(int argc, char *argv[])
{
	int res;
	int len;
	char tmp[512];
	/* Return non-zero only if you want to quit */
	if (argc < 1)
		return 0;
	if (!strcasecmp(argv[0], "quit") || !strcasecmp(argv[0], "exit")) {
		return -1;
	} else if (!strcasecmp(argv[0], "new")) {
		res = pc_session_new();
		printf("IAX session new: %d\n", res);
	} else if (!strcasecmp(argv[0], "newall")) {
		int x;
		for(x=0;x<PC_MAX_CALLS;x++)
			res = pc_session_new();
	} else if (!strcasecmp(argv[0], "call")) {
		if (argc != 5) {
			fprintf(stderr, "Usage: call <callno> <callerid> <dest> <language>\n");
			return 0;
		}
		res = pc_call(atoi(argv[1]), argv[2], argv[3], argv[4]);
		printf("Call on call %d from '%s' to '%s' (language %s) returned %d\n", atoi(argv[1]), argv[2], argv[3], argv[4], res);
	} else if (!strcasecmp(argv[0], "set")) {
		if (argc < 3) {
			fprintf(stderr, "Usage: set <variable> <value>\n");
			return 0;
		}
		res = pc_set_option(argv[1], argv[2], strlen(argv[2]));
		if (!res) 
			printf("Set variable '%s' to '%s' successfully\n", argv[1], argv[2]);
		else
			printf("Option set failed with error '%d'\n", res);
	} else if (!strcasecmp(argv[0], "audio-select")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: audio-select <channel>\n");
			return 0;
		}
		pc_select_audio(atoi(argv[1]));
	} else if (!strcasecmp(argv[0], "select")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: select <call>\n");
			return 0;
		}
		pc_select(atoi(argv[1]));
	} else if (!strcasecmp(argv[0], "conf")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: conf <call>\n");
			return 0;
		}
		pc_conference(atoi(argv[1]));
	} else if (!strcasecmp(argv[0], "answer")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: answer <call>\n");
			return 0;
		}
		pc_answer(atoi(argv[1]));
	} else if (!strcasecmp(argv[0], "ringa")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: ringa <call>\n");
			return 0;
		}
		pc_ring_announce(atoi(argv[1]));
	} else if (!strcasecmp(argv[0], "accept")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: accept <call>\n");
			return 0;
		}
		pc_accept(atoi(argv[1]));
	} else if (!strcasecmp(argv[0], "get")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: get <variable>\n");
			return 0;
		}
		memset(tmp, 0, sizeof(tmp));
		len = sizeof(tmp);
		res = pc_get_option(argv[1], tmp, &len);
		if (!res) 
			printf("Get variable '%s' is '%s' len %d\n", argv[1], tmp, len);
		else
			printf("Option get failed with error '%d'\n", res);
	} else if (!strcasecmp(argv[0], "info")) {
		struct peer_info info;
		if(!pc_peer_info(&info)){
			if(!info.calls)
				printf("No calls are up at this time\n");
			else  {
				int x;
				printf("There are %d calls up at this time\n",info.calls);
				printf("The following callno's are in use :");
				for(x=0;x<info.calls;x++)
					printf("%d ",info.callnos[x]);
				putchar('\n');
			}
		} else fprintf(stderr,"pc_peer_info failed\n");
				
		
	} else if (!strcasecmp(argv[0], "hangup")) {
		if (argc < 2) {
			fprintf(stderr, "Usage: hangup <call>\n");
			return 0;
		}
		pc_hangup(atoi(argv[1]), "Bye bye, sweety pie");
		printf("Hung up call %d\n", atoi(argv[1]));
	} else {
		fprintf(stderr, "Unknown command '%s'\n", argv[0]);
		return 0;
	}
	return 0;
}

static int handle_command_line(char *tmp)
{
	int argc;
	int quoted=0;
	char *pos;
	char *argv[MAX_ARGS];
	pos = tmp;
	argc = 0;
	for(;;) {
		while(*pos && (*pos < 33))
			pos++;
		if (!*pos) 
			break;
		if (*pos == '\"') {
			quoted = !quoted;
			pos++;
		}
		argv[argc++] = pos;
			while(*pos && ((*pos > 32) || quoted)) {
				if (*pos == '\"') {
					*pos = 0;
					quoted = !quoted;
					pos++;
				}
				pos++;
			}
		if (!*pos)
			break;
		*pos=0;
		pos++;
	}
	if (quoted) {
		fprintf(stderr, "Parse error, mismatched quote!\n");
		return 0;
	}
	return handle_command(argc, argv);
	
}

static int handle_console(void)
{
	char tmp[256];
	int len;
	int res;
	len = read(STDIN_FILENO, tmp, sizeof(tmp) - 1);
	if (len < 0) {
		fprintf(stderr, "Failed to read from stdin\n");
	}
	tmp[len] = '\0';
	if (len > 0)
		tmp[len-1] = '\0';
	res = handle_command_line(tmp);
	if (!res) print_prompt();
	return res;
}
 
int make_args_list(char *line,int maxargs,char **argv){
	char *traverse;
	int numargs=0;
	int newarg=1;
	int argsindex;
	if(line!=NULL){
		/* Initialize args list to NULLS*/
		for(argsindex=0;argsindex<maxargs;argsindex++) argv[argsindex]=NULL; 
		for(traverse=line;traverse!=NULL && *traverse!=0;traverse++){
			/* Upon encoutering a space ...*/
			if(*traverse==0x20){
				*traverse=0;/*Replace with null char*/
				newarg=1;
				continue;
			}
			if(newarg){
				/* Add to args list, maxargs is the maximum number of arguments */
				if(numargs<maxargs){
					argv[numargs++]=traverse;
					newarg=0;
				}
				/* No more room for any more args*/
				else break;
			}
		}
		/* got what we could from list let's go */
		return numargs;
	}
	return -1;
}
int script(char *filename){
	FILE *f=NULL;
	char line[255];
	char *lineptr=line;
	char *args[10]; int argc;/*)*/
	if(filename!=NULL){
		if((f=fopen(filename,"r"))!=NULL){
			bzero(line,sizeof(line));
			while(fgets(line,sizeof(line),f)!=NULL){
				int linelen=strlen(line);
#define DEBUG_ARGS
#if defined(DEBUG_ARGS)
				int argidx;
#endif
				lineptr=line;
				/* Ignore line comments */
				if(*line=='#'||*line==';'||(linelen>1 && *line=='/' && *(line+1)=='/'))
					continue;
				/* If line begins with ? poll user whether to run command\n */
				if(*line=='?'){
					char prompt='n';
					/* Whoops! gotta get rid of first character(?) */
					/* Makes things a little screwy this way so pay attention*/
					/* ie. don't use line for rest of function */
					linelen--; lineptr++;
					fprintf(stdout,"Do you want to run this command(y/n/maybe) \"%s\"?:",line);
					prompt=getchar();
					if(prompt=='n'||prompt=='N')
						continue;
				}
				/* Remove newline char from end of line*/
				if(linelen && lineptr[linelen-1]=='\n')lineptr[linelen-1]=0;
				argc=make_args_list(lineptr,10,args);
#if defined(DEBUG_ARGS)
				printf("Argc:%d ",argc);
				for(argidx=0;args[argidx]!=NULL && argidx<10;argidx++)
					printf("Arg %d \"%s\", ",argidx,(args[argidx]==NULL)?"NULL":args[argidx]);
				putchar('\n');
#endif
				handle_command(argc,args);
				bzero(line,sizeof(line));
			} 
			return 0;
		} else fprintf(stderr,"script: Unable to open file %s\n",filename);
	} else fprintf(stderr,"script: Filename Invalid\n");
	return -1;
}
int is_whitespace(char c){
	if( c==0x20 || c=='\t' || c=='\n')
		return 1;
	return 0;
}
int main(int argc, char *argv[])
{
	fd_set rfds;
	int max;
	int res;
	int callno=-1;
#ifdef SNOM_HACK
	char *modules = "audio-snomphone.so";
#else 
	char *modules = "audio-oss.so";
#endif
	char *scriptname=NULL;
	char *config_file=NULL;
	int x;


	if (argc>1){
		for(x=1;x<argc;x++){
			if(argv[x][0]=='-'){
				if(argv[x][1]=='s' && x++<argc)
					scriptname=argv[x];
				else if (argv[x][1]=='c' && x++<argc)
					config_file=argv[x];
			} else {
				modules = argv[x];
				break;
			}
		}
	}
	printf("Scriptname %s Config_File %s module %s\n",scriptname,config_file,modules);

	/* Launch phonecore */
	if ((portno = pc_init(modules)) < 0) {
		fprintf(stderr, "Phonecore initialization failed :(\n");
		exit(1);
	} 
	pc_select_audio(0);
	printf("Phonecore initialized on port %d", portno);
	if (modules)
		printf(" (modules = '%s')\n", modules);
	printf("\n");
	if(scriptname!=NULL)
		script(scriptname);
	print_prompt();

	if(config_file){
		FILE *file;
		file=fopen(config_file,"r");
		if(file!=NULL){
			char linebuf[255];
			char var[255];
			char value[255];
			int offset,x;
			bzero(linebuf,sizeof(linebuf));
			bzero(var,sizeof(var));
			bzero(value,sizeof(value));
			while( NULL != fgets(linebuf,sizeof(linebuf),file)){
				if(*linebuf=='#')
					continue;
				fprintf(stderr,linebuf);
				for(x=0,offset=0;x<sizeof(linebuf)&&!is_whitespace(linebuf[x]);x++);
				strncpy(var,linebuf+offset,x-offset);
				var[x-offset]=0;
				for(;x<sizeof(linebuf)&&is_whitespace(linebuf[x]);x++);
				for(offset=x;x<sizeof(linebuf)&&!is_whitespace(linebuf[x]);x++);
				strncpy(value,linebuf+offset,x-offset);
				value[x-offset]=0;
				pc_set_option(var,value,sizeof(value));
			}
		}
	}

	
	for (;;) {
		FD_ZERO(&rfds);
		FD_SET(STDIN_FILENO, &rfds);
		FD_SET(pc_get_fd(), &rfds);
		max = STDIN_FILENO;
		if (pc_get_fd() > max)
			max = pc_get_fd();
		res = select(max + 1, &rfds, NULL, NULL, NULL);
		if (res < 0) {
			fprintf(stderr, "Select failed: %s\n", strerror(errno));
			continue;
		}
		if (FD_ISSET(STDIN_FILENO, &rfds)) {
			if (handle_console())
				break;
		}
		if (FD_ISSET(pc_get_fd(), &rfds)) {
			if (handle_pcevent())
					break;
		}
	}
	
	pc_cleanup();
	return 0;
}
