/** reboot.fm re_jackmixer
 *
 *  Copyright (C) 2004 jan gerber <j@reboot.fm>
 *
 *  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.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 *  structure as of now:
 *  - opens 2* CHANNELS input ports
 *             in and fallback
 *  - 2* CHANNELS output ports
 *  - checks for sound in in if none switches
 *    after DELAY output to fallback
 *
 * TODO:
 * - command line options for sane_silence_value,delay,channels
 * - crossfade?
 *   tihs could be usefull:
 *	cvs -d:pserver:anonymous@airchain.salemradiolabs.com:/home/cvs co jackswitch
 * - look into jack_set_port_registration_callback 
 *      (jack_client_t *, JackPortRegistrationCallback registration_callback, void *arg);
 *   [ done with jack.plumbing now ]
 */

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <syslog.h>

#include <jack/jack.h>

#define CHANNELS 2
#define SANE_SILENCE_VALUE 0.001
#define DELAY 12
#define STATUS_IN 1
#define STATUS_FALLBACK 2
#define SYSTEMLOG_NAME "re_jackmixer"
#define DEFAULT_STATUS "/dev/shm/jackmixer.status"

jack_port_t *in_ports[CHANNELS];
jack_port_t *fallback_ports[CHANNELS];
jack_port_t *out_ports[CHANNELS];

time_t last_noise=-1;
int status=0;

/**
 * The process callback for this JACK application.
 * It is called by JACK at the appropriate times.
 */
int process (jack_nframes_t nframes, void *arg)
{
	int i;
	time_t now=time(NULL);
	jack_default_audio_sample_t max = 0.0f;
	jack_default_audio_sample_t *out[CHANNELS];
	jack_default_audio_sample_t *in[CHANNELS];
	jack_default_audio_sample_t *fallback[CHANNELS];
	jack_default_audio_sample_t **tosend;

	/* get pointers to audio buffers */
	for(i=0;i<CHANNELS;i++){
		out[i] = (jack_default_audio_sample_t *) jack_port_get_buffer (out_ports[i], nframes);
		in[i] = (jack_default_audio_sample_t *) jack_port_get_buffer (in_ports[i], nframes);
		fallback[i] = (jack_default_audio_sample_t *) jack_port_get_buffer (fallback_ports[i], nframes);
	}
	
	/* check for noise on input port */
	for(i=0;i<nframes;i++)
		if(max<in[0][i])
			max=in[0][i];

	if(max > SANE_SILENCE_VALUE){
		last_noise=now;
		
	}
	if((now-last_noise)>DELAY  || last_noise==-1){
		tosend=fallback;
		status=STATUS_FALLBACK;
	}
	else{
		tosend=in;
		status=STATUS_IN;
	}
	
	/* copy the audio samples to out */
	for(i=0;i<CHANNELS;i++){
		memcpy (out[i], tosend[i], sizeof (jack_default_audio_sample_t) * nframes);
	}

	return 0;      
}

/**
 * This is the shutdown callback for this JACK application.
 * It is called by JACK if the server ever shuts down or
 * decides to disconnect the client.
 */
void jack_shutdown (void *arg)
{
	exit (1);
}


void usage(){
	// fprintf(stderr, "re_jackmixer usage: re_jackmixer [-o output] [-i input] [-f fallback] [-s statusfile]\n");
	fprintf(stderr, "re_jackmixer usage: re_jackmixer [-s statusfile]\n");
	fprintf(stderr, "                    connect apps to the mixer with jack.plumbing\n");
	fprintf(stderr, "                    statusfile defaults to %s\n",DEFAULT_STATUS);
	exit(-1);
}



void update_statusfile(char* statusfile,char *string){
	FILE *f = fopen (statusfile,"w");
	 if (f)
        {
            fprintf (f, "%s",string);
            fclose (f);
        }
}

int main (int argc, char *argv[]){
	int i;
	int c;
	int current_status=-1;
	jack_client_t *client;
	char statusfile[1024];
	char port_name[32];
	/*
	char output_client[32],input_client[32],fallback_client[32];
	sprintf(output_client, "ices*");
	sprintf(input_client, "player_in*");
	sprintf(fallback_client, "player_fallback*");
	*/
	sprintf(statusfile, DEFAULT_STATUS);
	

	while( (c = getopt(argc, argv, "s:o:i:f:h")) != EOF){
		switch(c){
			case 'h':
				usage();
				break;
			case 's':
				sprintf(statusfile, "%s*",optarg);
				break;
			/*
			case 'o':
				sprintf(output_client, "%s*",optarg);
				break;
			case 'i':
				sprintf(input_client, "%s*",optarg);
				break;
			case 'f':
				sprintf(fallback_client, "%s*",optarg);
				break;
			*/
		}
	}

	/* try to become a client of the JACK server */
	if ((client = jack_client_new ("re_jackmixer")) == 0) {
		fprintf (stderr, "jack server not running?\n");
		return 1;
	}

	/* tell the JACK server to call `process()' whenever
	   there is work to be done.
	*/

	jack_set_process_callback (client, process, 0);

	/* tell the JACK server to call `jack_shutdown()' if
	   it ever shuts down, either entirely, or if it
	   just decides to stop calling us.
	*/

	jack_on_shutdown (client, jack_shutdown, 0);

	/* display the current sample rate. 
	 */

	printf ("engine sample rate: %d\n",jack_get_sample_rate (client));

    /* Create and connect the jack ports */
	/* in,fallback,out */
    for (i = 0; i < CHANNELS; i++) 
    {
        sprintf(port_name, "in_%d", i+1);
        in_ports[i] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput,0); 
		sprintf(port_name, "fallback_%d", i+1);
		fallback_ports[i] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput,0); 
		sprintf(port_name, "out_%d", i+1);
		out_ports[i] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput,0); 
    }

	/* tell the JACK server that we are ready to roll */
	if (jack_activate (client)) {
		fprintf (stderr, "cannot activate client");
		return 1;
	}

	/* connect the ports. Note: you can't do this before
	   the client is activated, because we can't allow
	   connections to be made to clients that aren't
	   running.
	
		this is inacitve since jack.plumbing makes a better job.
	*/
	/*
	char **ports;
	// input
	if ((ports = jack_get_ports (client, input_client, NULL, JackPortIsOutput)) == NULL) {
		fprintf(stderr, "unable to connect to %s\n",input_client);
	}
	else {
		for(i=0;i<CHANNELS;i++){
			if (jack_connect (client, ports[i], jack_port_name (in_ports[i]))) {
				fprintf (stderr, "error connecting to %s \n",ports[i]);
			}
		}
	}
	
	// fallback
	if ((ports = jack_get_ports (client, fallback_client, NULL, JackPortIsOutput)) == NULL) {
		fprintf(stderr, "unable to connect to %s\n",fallback_client);
	}
	else {
		for(i=0;i<CHANNELS;i++){
			if (jack_connect (client, ports[i], jack_port_name (fallback_ports[i]))) {
				fprintf (stderr, "error connecting fallback to %s \n",ports[i]);
			}
		}
	}

	// output 
	if ((ports = jack_get_ports (client, output_client, NULL, JackPortIsInput)) == NULL) {
		fprintf(stderr, "unable to connect output to %s\n",output_client);
		fprintf(stderr, "lets count on jack.plumbing to do the work.\n");
		//exit(1);
	}
	else{
		for(i=0;i<CHANNELS;i++){
			if (jack_connect (client, jack_port_name (out_ports[i]), ports[i])) {
				fprintf (stderr, "cannot connect to ices ports\n");
			}
		}
	} 
	free (ports);
	*/
	
	openlog(SYSTEMLOG_NAME,LOG_NOWAIT,LOG_USER);
	syslog(LOG_NOTICE, "started\n");
	/* run for 100 days of radio */
	while(1) {
		if(status != current_status){
			current_status=status;
			if(status==STATUS_IN){
				syslog(LOG_NOTICE, "switch to in ports\n");
				update_statusfile(statusfile,"in");
			}
			else if(status==STATUS_FALLBACK){
				syslog(LOG_NOTICE, "switch to fallback ports\n");
				update_statusfile(statusfile,"fallback");
			}
		}
		//sleep (10000);
		sleep (1);
	}
	jack_client_close (client);
	closelog();
	exit (0);
}
