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

    FILE....: envtone.cpp
    TYPE....: C++ Module
    AUTHOR..: David Rowe
    DATE....: 1/11/01

    Routines for programming the VPB call progress detector from tones 
    specified by environment variables.


         Voicetronix Voice Processing Board (VPB) Software

         Copyright (C) 1999-2007 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., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

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

#include <assert.h>
#include "vpbapi.h"
#include "vpbtoned.h"
#include "mess.h"

#include <cstdlib>
#include <cstring>

// +/- % this value tolerance in on/off time for pulsed tones

#define TONE_TOL 25


static char *next_arg_str(char *p, char *str) {
	char *pnew;
	int  len;

	*str = 0;

	if (p == NULL) return NULL;

	pnew = strchr(p, ',');
	if (pnew != NULL) {     //  comma found
		len = pnew-p;
		pnew++;
	}
	else
		len = strlen(p);      // last value in string

	memcpy(str, p, len);
	str[len] = 0;

	return pnew;
}

static char *next_arg_int(char *p, int *i) {
	char tmp[VPB_MAX_STR];
	int  itmp;

	p = next_arg_str(p, tmp);
	itmp = atoi(tmp);
	*i = itmp;

	return p;
}

static void init_tone_continuous(VPB_DETECT *cont) {

	// Continuous tone detector prototype (e.g. dial tone)

	cont->nstates        = 2;
	cont->tone_id        = VPB_DIAL;
	cont->ntones         = 1;
	cont->freq1          = 400;
	cont->bandwidth1     = 100;
	cont->freq2          = 0;
	cont->bandwidth2     = 0;
	cont->minlevel1      = -40;
	cont->minlevel2      = 0;
	cont->twist          = 0;
	cont->snr            = 10;
	cont->glitch         = 40;

	cont->stran[0].type  = VPB_RISING;
	cont->stran[0].tfire = 0;
	cont->stran[0].tmin  = 0;
	cont->stran[0].tmax  = 0;
	cont->stran[1].type  = VPB_TIMER;
	cont->stran[1].tfire = 2000;
	cont->stran[1].tmin  = 0;
	cont->stran[1].tmax  = 0;
}

static void init_tone_pulsed(VPB_DETECT *pulsed) {
	// Pulsed tone detector prototype (e.g. busy tone)

	pulsed->nstates        = 2;
	pulsed->tone_id        = VPB_BUSY;
	pulsed->ntones         = 1;
	pulsed->freq1          = 400;
	pulsed->bandwidth1     = 200;
	pulsed->freq2          = 0;
	pulsed->bandwidth2     = 0;
	pulsed->minlevel1      = -40;
	pulsed->minlevel2      = 0;
	pulsed->twist          = 0;
	pulsed->snr            = 10;
	pulsed->glitch         = 40;

	pulsed->stran[0].type  = VPB_RISING;
	pulsed->stran[0].tfire = 0;
	pulsed->stran[0].tmin  = 0;
	pulsed->stran[0].tmax  = 0;
	pulsed->stran[1].type  = VPB_FALLING;
	pulsed->stran[1].tfire = 0;
	pulsed->stran[1].tmin  = 200;
	pulsed->stran[1].tmax  = 300;

	// optional third state (P2 type tones only)
	pulsed->stran[2].type  = VPB_RISING;
	pulsed->stran[2].tfire = 0;
	pulsed->stran[2].tmin  = 200;
	pulsed->stran[2].tmax  = 300;
}


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

	FUNCTION: envtone_read_tone_from_env
	AUTHOR..: David Rowe
	DATE....: 1/11/01

	Reads environment variables to program tone detectors.  Allows
	configuring up to 3 tone detectors to be set without compiling,
	or writing code.
	
	JK 15/04/02 Modified to support changes to twist characteristics 
	(Allowable magnitude difference between the dual tones). Max allowable
	twist = 20 dB.
	JK 7/04/02 Modified to support dual tone characteristics and changes to
	the SNR threshold on the tone detector. This can be done by specifying
	P3, P4 or P5 in the second argument. 
			
	Env. variables defining tones are defined as follows (bash shell):

	export VPB_TONE[0..2]=<DefN>,<Type>,<Freq Hz>,<BandWidth Hz>,<Time fire/on>,<Time off>\
				[,<Freq2 Hz>,<Bandwidth2 Hz>,<SNR dB>,[<Twist dB>]]
	
	Where:
		<DefN>		=	[BUSY | DIAL | RINGBACK | GRUNT | [5..9] ]
		<Type> 		= 	[C | P | P2 | P3 | P4 | P5 | P6 | P7 | P8]
		<Time fire/on> 	= 	[Tfirems | Tonms]


	Note: 
	      Toffms is only used if Type is P2
	      
	      Tfirems, Freq2 Hz, BW2 Hz and SNR dB is only used if Type is P3

	      Tonms, Freq2 Hz, BW2 Hz and SNR dB is only used if Type is P4
    
	      Tonms, Toffms, Freq2 Hz, BW2 Hz and SNR dB is only used if Type is P5 

	      Tfirems, Freq2 Hz, BW2 Hz, SNR dB & Twist dB is only used if Type is P6 

	      Tonms, Freq2 Hz, BW2 Hz, SNR dB & Twist dB is only used if Type is P7 
    
	      Tonms, Toffms, Freq2 Hz, BW2 Hz, SNR dB & Twist dB is only used if Type is P8

  
	e.g. a 400 Hz continuous dial tone, 50Hz bandwidth, fire after 
	1000ms (1 sec):

	export VPB_TONE=DIAL,C,400,50,1000

	e.g. a 500Hz busy tone, 100 Hz bandwidth, pulsed with a 350ms on time:

	export VPB_TONE=BUSY,P,500,100,350

	e.g. a 80ms on, 120ms off busy tone, 100 Hz bandwidth, pulsed 500Hz.

	export VPB_TONE=BUSY,P2,500,100,80,120

	e.g. a harmonic busy tone with fundamental frequency of 400Hz, 
	100Hz bandwidth pulsed with a 360ms on time. Note the 3rd harmonic is
	1200Hz. The signal to noise ratio (SNR) is 10dB.

	export VPB_TONE=BUSY,P4,400,100,360,1200,100,10

	e.g. a continuous harmonic dial tone with a fundamental frequency of 
	400Hz, 100Hz bandwidth and fires after 2sec. Note the 3rd harmonic is 
	1200Hz (3 x fundamental frequency). The signal to nose ratio (SNR) is 
	6dB.

	export VPB_TONE=DIAL,P3,400,100,2000,1200,100,6

	e.g. a continuous dial tone with frequencies of 400Hz and 1000Hz, 
	100Hz bandwidth and fires after 2sec. The signal to nose ratio (SNR)
	is 6dB while the twist is 10dB.

	export VPB_TONE=DIAL,P6,400,100,2000,1000,100,6,10
	
	You can define more than one tone:

	export VPB_TONE=BUSY,P2,500,100,80,120
	export VPB_TONE1=DIAL,C,400,50,1000
	export VPB_TONE2=5,C,400,50,1000

	If you are unsure of bandwidth just use 100.

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

static void envtone_read(int handle, const char *env_name) {
	char       *p;
	char       tone_id_str[VPB_MAX_STR];
	char       cont_or_pulsed[VPB_MAX_STR];
	int        freq, bw, ontime, tone_id;
	int        offtime = 0;
	int        freq2 = 0;
	int        bw2 = 0;
	int        snr = 0;
	int        twist = 0;
	int        tol;
	VPB_DETECT tone;

	p = getenv(env_name);
	if (p == NULL) {
	    return;
	}
	p = next_arg_str(p, tone_id_str);
	p = next_arg_str(p, cont_or_pulsed);
	p = next_arg_int(p, &freq);
	p = next_arg_int(p, &bw);
	p = next_arg_int(p, &ontime);
        if ((strcmp(cont_or_pulsed, "C")==0) ||
	  (strcmp(cont_or_pulsed, "P")==0)){
	  mprintf("VPB_TONE loaded from environment:\n%s,%s,%d,%d,%d\n",
		tone_id_str, cont_or_pulsed, freq, bw, ontime);
	}
	if (strcmp(cont_or_pulsed, "P2")==0) {
	  p = next_arg_int(p, &offtime);
  	  mprintf("VPB_TONE loaded from environment:\n%s,%s,%d,%d,%d,%d\n",
		tone_id_str, cont_or_pulsed, freq, bw, ontime,offtime);
	}
	if ((strcmp(cont_or_pulsed, "P3")==0) ||
	  (strcmp(cont_or_pulsed, "P4")==0)){
	  p = next_arg_int(p,&freq2);
	  p = next_arg_int(p,&bw2);
	  p = next_arg_int(p,&snr);
	  mprintf("VPB_TONE loaded from environment:\n%s,%s,%d,%d,%d,%d,%d,%d\n",
		tone_id_str, cont_or_pulsed, freq, bw, ontime, freq2, bw2, snr);
	}
	if (strcmp(cont_or_pulsed, "P5")==0){
	  p = next_arg_int(p,&offtime);
	  p = next_arg_int(p,&freq2);
	  p = next_arg_int(p,&bw2);
	  p = next_arg_int(p,&snr);
	  mprintf("VPB_TONE loaded from environment:\n%s,%s,%d,%d,%d,%d,%d,%d,%d\n",
		tone_id_str, cont_or_pulsed, freq, bw, ontime, offtime, freq2, bw2, snr);
	}
	if ((strcmp(cont_or_pulsed, "P6")==0) ||
	  (strcmp(cont_or_pulsed, "P7")==0)){
	  p = next_arg_int(p,&freq2);
	  p = next_arg_int(p,&bw2);
	  p = next_arg_int(p,&snr);
	  p = next_arg_int(p,&twist);

	  mprintf("VPB_TONE loaded from environment:\n%s,%s,%d,%d,%d,%d,%d,%d,%d\n",
		tone_id_str, cont_or_pulsed, freq, bw, ontime, freq2, bw2, snr, twist);
	}
	if (strcmp(cont_or_pulsed, "P8")==0){
	  p = next_arg_int(p,&offtime);
	  p = next_arg_int(p,&freq2);
	  p = next_arg_int(p,&bw2);
	  p = next_arg_int(p,&snr);
	  p = next_arg_int(p,&twist);
	  mprintf("VPB_TONE loaded from environment:\n%s,%s,%d,%d,%d,%d,%d,%d,%d,%d\n",
		tone_id_str, cont_or_pulsed, freq, bw, ontime, offtime, freq2, bw2, snr, twist);
	}



	// set tone id

	if (strcmp(tone_id_str,"DIAL")==0)
		tone_id = VPB_DIAL;
	else
	if (strcmp(tone_id_str,"BUSY")==0)
		tone_id = VPB_BUSY;
	else
	if (strcmp(tone_id_str,"RINGBACK")==0)
		tone_id = VPB_RINGBACK;
	else
	if (strcmp(tone_id_str,"GRUNT")==0)
		tone_id = VPB_GRUNT;
	else {
		// must be numeric tone id, (VPB_GRUNT+1)..(VPB_MD-1)
		tone_id = atoi(tone_id_str);
//		assert(tone_id > VPB_GRUNT);
		assert(tone_id < VPB_MD);
	}

	if (strcmp(cont_or_pulsed, "C") == 0) {
		init_tone_continuous(&tone);
		tone.tone_id = tone_id;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tone.stran[1].tfire = ontime;
	}
	if (strcmp(cont_or_pulsed, "P") == 0) {
		init_tone_pulsed(&tone);
		tone.tone_id = tone_id;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tol = (int)((float)(ontime*(float)TONE_TOL/100.0));
		tone.stran[1].tmin = ontime - tol;
		tone.stran[1].tmax = ontime + tol;
	}
        if (strcmp(cont_or_pulsed, "P2") == 0) {
		init_tone_pulsed(&tone);
		tone.tone_id = tone_id;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tol = (int)((float)(ontime*(float)TONE_TOL/100.0));
		tone.stran[1].tmin = ontime - tol;
		tone.stran[1].tmax = ontime + tol;
		// optional third state
		mprintf("third busy state: off = %d\n", offtime);
		tone.nstates = 3;
		tone.stran[2].tmin = offtime - tol;
		tone.stran[2].tmax = offtime + tol;
	}

        if (strcmp(cont_or_pulsed, "P3") == 0) {
		init_tone_continuous(&tone);
		tone.tone_id = tone_id;
		tone.ntones = 2;
		tone.minlevel2 = -40;
		tone.twist = 20;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tone.freq2 = freq2;
		tone.bandwidth2 = bw2;
		tone.snr = snr;
		tone.stran[1].tfire = ontime;
	}
        if (strcmp(cont_or_pulsed, "P4") == 0) {
		init_tone_pulsed(&tone);
		tone.tone_id = tone_id;
		tone.ntones = 2;
		tone.minlevel2 = -40;
		tone.twist = 20;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tone.freq2 = freq2;
		tone.bandwidth2 = bw2;
		tone.snr = snr;
		tol = (int)((float)(ontime*(float)TONE_TOL/100.0));
		tone.stran[1].tmin = ontime - tol;
		tone.stran[1].tmax = ontime + tol;
	}
        if (strcmp(cont_or_pulsed, "P5") == 0) {
		init_tone_pulsed(&tone);
		tone.tone_id = tone_id;
		tone.ntones = 2;
		tone.minlevel2 = -40;
		tone.twist = 20;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tone.freq2 = freq2;
		tone.bandwidth2 = bw2;
		tone.snr = snr;
		tol = (int)((float)(ontime*(float)TONE_TOL/100.0));
		tone.stran[1].tmin = ontime - tol;
		tone.stran[1].tmax = ontime + tol;
		// optional third state
		mprintf("third busy state: off = %d\n", offtime);
		tone.nstates = 3;
		tone.stran[2].tmin = offtime - tol;
		tone.stran[2].tmax = offtime + tol;
	}
        if (strcmp(cont_or_pulsed, "P6") == 0) {
		init_tone_continuous(&tone);
		tone.tone_id = tone_id;
		tone.ntones = 2;
		tone.minlevel2 = -40;
		tone.twist = twist;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tone.freq2 = freq2;
		tone.bandwidth2 = bw2;
		tone.snr = snr;
		tone.stran[1].tfire = ontime;
	}
        if (strcmp(cont_or_pulsed, "P7") == 0) {
		init_tone_pulsed(&tone);
		tone.tone_id = tone_id;
		tone.ntones = 2;
		tone.minlevel2 = -40;
		tone.twist = twist;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tone.freq2 = freq2;
		tone.bandwidth2 = bw2;
		tone.snr = snr;
		tol = (int)((float)(ontime*(float)TONE_TOL/100.0));
		tone.stran[1].tmin = ontime - tol;
		tone.stran[1].tmax = ontime + tol;
	}
        if (strcmp(cont_or_pulsed, "P8") == 0) {
		init_tone_pulsed(&tone);
		tone.tone_id = tone_id;
		tone.ntones = 2;
		tone.minlevel2 = -40;
		tone.twist = twist;
		tone.freq1 = freq;
		tone.bandwidth1 = bw;
		tone.freq2 = freq2;
		tone.bandwidth2 = bw2;
		tone.snr = snr;
		tol = (int)((float)(ontime*(float)TONE_TOL/100.0));
		tone.stran[1].tmin = ontime - tol;
		tone.stran[1].tmax = ontime + tol;
		// optional third state
		mprintf("third busy state: off = %d\n", offtime);
		tone.nstates = 3;
		tone.stran[2].tmin = offtime - tol;
		tone.stran[2].tmax = offtime + tol;
	}
	settonedet(handle, tone);
}

void read_tones_from_env(int handle) {
	envtone_read(handle, "VPB_TONE");
	envtone_read(handle, "VPB_TONE1");
	envtone_read(handle, "VPB_TONE2");
	envtone_read(handle, "VPB_TONE3");
	envtone_read(handle, "VPB_TONE4");
	envtone_read(handle, "VPB_TONE5");
	envtone_read(handle, "VPB_TONE6");
	envtone_read(handle, "VPB_TONE7");
	envtone_read(handle, "VPB_TONE8");
	envtone_read(handle, "VPB_TONE9");
}

