/*
 * Gnophone: A client for the Asterisk PBX
 *
 * Copyright (C) 2000-2005, Digium, Inc.
 *
 * Written by Mark Spencer
 *
 * Linux/UNIX version distributed under the terms of
 * the GNU General Public License
 *
 * audio-control.c: Generic Audio/Telephony Interface
 *
 */

#include "gnophone.h"

/* Target a peak at 10000 for normal speech */
#define TARGET 10000.0
#define DELTA 0.01
#define PASSES 10

static char INSTRUCTIONS[] = 
"Read this sentence over and over in a normal voice as though\n"
"you were talking to someone over the internet.  We want the level\n"
"of your sound to be at half the level on the bar.";

GtkWidget *audiobox = NULL;

extern GtkWidget *mw;
/*GtkWidget *audiobox;*/
GtkWidget *configure;
GtkWidget *abutton;

GtkWidget *hscale, *hscale2, *hscale3;
GtkObject *adj, *adj2, *adj3;

GtkWidget *aaframe;
GtkWidget *level;
GtkWidget *instruct;

GtkWidget *autoadj;

int mixer_fd = 0;
int micvol, mainvol, recvol;

int audioc = -1;

static char *get_audio_dev(int id);
static void update_mixer_mainvol(GtkAdjustment *);
static void update_mixer_micvol(GtkAdjustment *);
static void update_mixer_recvol(GtkAdjustment *);
static void open_mixerfd(void);

void (*other_loudness)(int level);


static int audiodestroy(void)
{
	gtk_grab_remove(audiobox);
	other_loudness = NULL;
	audiobox = NULL;
	return TRUE;
}

#if 0
static void audio_cancel(void)
{
	gtk_widget_destroy(audiobox);
}

static void audio_okay(void)
{
	gtk_widget_destroy(audiobox);
}
#endif
static int showmenu(GtkMenu *m, GdkEvent *event)
{
    if (event->type == GDK_BUTTON_PRESS) {
		GdkEventButton *bevent = (GdkEventButton *) event; 
		gtk_menu_popup (m, NULL, NULL, NULL, NULL,
                              bevent->button, bevent->time);
		return TRUE;
    }
    return FALSE;
}

static void audiobox_response(GtkWidget *w, int response, gpointer data)
{
	gtk_widget_destroy(audiobox);
}

static void select_driver(GtkWidget *w, long c)
{
	if (audioc >= 0)
		fprintf(stderr, "User selected channel %s\n", get_audio_dev(audioc));
	iaxc_audio_devices_set(c,c,c);
	audioc = c;
	
	if (audioc > -1)
		gtk_label_set_text(GTK_LABEL(GTK_BIN(abutton)->child), get_audio_dev(audioc));
	else
		gtk_label_set_text(GTK_LABEL(GTK_BIN(abutton)->child), "No device selected");
}

#if 0
static float historicmax = 0.0;
static int goodsamps = 0;
static int pass = 0;

static void update_prog_bar(int lev)
{
	int max = lev;
	float amt;
	if (max > historicmax)
		historicmax = max;
	else
		historicmax = (1 - DELTA) * historicmax + DELTA * (float)max;
	amt = historicmax / (TARGET * 2.0);
	if (amt > 1.0)
		amt = 1.0;
	printf("historicmax = %d, %f, %f, %d\n", max, historicmax, amt, goodsamps);
	if (!(pass++ % PASSES)) {
		if (amt < 0.5) {
			goodsamps = 0;
			if ((recvol >> 8) < 100)
				gtk_adjustment_set_value(GTK_ADJUSTMENT(adj3), (recvol >> 8) + 1);
			else
				gtk_adjustment_set_value(GTK_ADJUSTMENT(adj2), (micvol >> 8) + 1);
		} else if (amt > 0.7) {
			goodsamps = 0;
			if ((micvol >> 8) > 0)
				gtk_adjustment_set_value(GTK_ADJUSTMENT(adj2), (micvol >> 8) - 1);
			else
				gtk_adjustment_set_value(GTK_ADJUSTMENT(adj3), (recvol >> 8) - 1);
			/* Reset historic max */
			historicmax *= 0;
		} else 
			goodsamps++;
		if (goodsamps > 10)
			gtk_button_clicked(GTK_BUTTON(autoadj));
	}
	gtk_progress_bar_update(GTK_PROGRESS_BAR(level), amt);
	return;
}

static void audio_adjust(void)
{
	goodsamps = 0;
	pass = 0;
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(autoadj))) {

		gtk_widget_set_sensitive(GTK_WIDGET(instruct), TRUE);
		gtk_widget_set_sensitive(GTK_WIDGET(level), TRUE);
		gtk_widget_set_sensitive(GTK_WIDGET(aaframe), TRUE);
		other_loudness = update_prog_bar;
	} else {
		gtk_widget_set_sensitive(GTK_WIDGET(instruct), FALSE);
		gtk_widget_set_sensitive(GTK_WIDGET(level), FALSE);
		gtk_widget_set_sensitive(GTK_WIDGET(aaframe), FALSE);
		gtk_progress_bar_update(GTK_PROGRESS_BAR(level), 0);
		gtk_container_set_resize_mode(GTK_CONTAINER(audiobox), GTK_RESIZE_QUEUE);
		gtk_container_check_resize(GTK_CONTAINER(audiobox));
		other_loudness = NULL;
	}
		
}
#endif

static int get_audio_dev_count(void)
{
	int nDevs, input, output, ring;
	struct iaxc_audio_device *devs;

	iaxc_audio_devices_get(&devs, &nDevs, &input, &output, &ring);

	return nDevs;
}

static char *get_audio_dev(int id)
{
	static char buf[256]="";
	struct iaxc_audio_device *devs;
	int nDevs, input, output, ring;

	iaxc_audio_devices_get(&devs, &nDevs, &input, &output, &ring);
	if (id < nDevs) {
		strncpy(buf, devs[id].name, sizeof(buf) - 1);
	}
	return buf;
}

static int build_option_screen(void)
{
	GtkWidget *vbox;
	GtkWidget *frame;
	GtkWidget *outerbox;
	GtkWidget *menu;
	GtkWidget *mi;
	GtkWidget *labels;
	GtkWidget *table;
	GtkWidget *abox;
	GtkWidget *tmpbox ;
	
	int ac = 0;
	int cnt = 0;
	char *tmp = NULL;
	
	audiobox = gtk_dialog_new();
	gtk_window_set_title(GTK_WINDOW(audiobox),"Gnophone: Audio settings");
	gtk_dialog_add_buttons(GTK_DIALOG(audiobox), "Accept", GTK_RESPONSE_ACCEPT,
			"Cancel", GTK_RESPONSE_REJECT, NULL);
/*	gtk_window_set_title(GTK_WINDOW(audiobox), "Gnophone: Sound settings");
	gtk_widget_realize(GTK_WIDGET(audiobox));*/
	g_signal_connect(GTK_OBJECT(audiobox), "destroy", GTK_SIGNAL_FUNC(audiodestroy), NULL);
	g_signal_connect(GTK_OBJECT(audiobox), "response", GTK_SIGNAL_FUNC(audiobox_response), NULL);
	/* Build a vbox */
	vbox = gtk_vbox_new(FALSE, 5);
	tmpbox = gtk_vbox_new(FALSE, 5);	
	
/*	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(audiobox)->vbox), tmpbox);
*/
	/* Build a frame for the adjustments */
	frame = gtk_frame_new("Audio Device");

	cnt = get_audio_dev_count();

	/* Make a button for our menu */
	if (audioc > -1) 
		abutton = gtk_button_new_with_label(get_audio_dev(audioc));
	else {
		if (!cnt) {
			abutton = gtk_button_new_with_label("No Audio Devices Detected");
			gtk_widget_set_sensitive(abutton, FALSE);
		} else
			abutton = gtk_button_new_with_label("No Device Selected");
	}

	/* Build the menu of audio devices */
	menu = gtk_menu_new();
	while(strlen((tmp = get_audio_dev(ac)))) {
		mi = gtk_menu_item_new_with_label(tmp);
		gtk_widget_show(mi);
		g_signal_connect(GTK_OBJECT(mi), "activate", GTK_SIGNAL_FUNC(select_driver), (void *)(long)ac);
		gtk_menu_append(GTK_MENU(menu), mi);
		ac++;
		if (ac == cnt)
			break;
	}

	g_signal_connect_object(GTK_OBJECT(abutton), "event", GTK_SIGNAL_FUNC (showmenu), GTK_OBJECT(menu), G_CONNECT_SWAPPED);	
	/* Put the button in the vbox */
	gtk_box_pack_start(GTK_BOX(vbox), abutton, FALSE, FALSE, 5);
	
	/* Add a border to the vbox and put it in the frame */
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
	gtk_container_add(GTK_CONTAINER(frame), vbox);

	/* An outer box for the frame and the buttons */
	outerbox = gtk_vbox_new(FALSE, 5);
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(audiobox)->vbox), outerbox);
	gtk_box_pack_start(GTK_BOX(outerbox), frame, FALSE, FALSE, 5);

	/* Build a frame for the adjustments */
	frame = gtk_frame_new("Audio Mixer");
	vbox = gtk_vbox_new(FALSE, 5);

	table = gtk_table_new(3, 2, FALSE);
	gtk_table_set_row_spacings(GTK_TABLE(table), 5);
	gtk_table_set_col_spacings(GTK_TABLE(table), 5);
	gtk_container_set_border_width(GTK_CONTAINER(table), 5);
	gtk_widget_show(table);

	gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);

	adj = gtk_adjustment_new (0,0,110,10,10,10);
	g_signal_connect(GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(update_mixer_mainvol), NULL);

	/* Main Volume */
	labels = gtk_label_new("Main volume:");
	gtk_widget_show(labels);

	hscale = gtk_hscale_new(GTK_ADJUSTMENT (adj));
	gtk_widget_show(hscale);

	gtk_table_attach(GTK_TABLE(table), labels, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(table), hscale, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);

	/* Misc Volume */
	labels = gtk_label_new("Mic. volume:");
	gtk_widget_show(labels);

	adj2 = gtk_adjustment_new(0,0,110,10,10,10);
	g_signal_connect(GTK_OBJECT(adj2), "value_changed", GTK_SIGNAL_FUNC(update_mixer_micvol), NULL);

	hscale2 = gtk_hscale_new(GTK_ADJUSTMENT(adj2));
	gtk_widget_show(hscale2);

	gtk_table_attach(GTK_TABLE(table), labels, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(table), hscale2, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);

	/* Rec. Volume */
	labels = gtk_label_new("Rec. volume:");
	gtk_widget_show(labels);

	adj3 = gtk_adjustment_new(0,0,110,10,10,10);
	g_signal_connect(GTK_OBJECT(adj3), "value_changed", GTK_SIGNAL_FUNC(update_mixer_recvol), NULL);

	hscale3 = gtk_hscale_new(GTK_ADJUSTMENT(adj3));
	gtk_widget_show(hscale3);

	gtk_table_attach(GTK_TABLE(table), labels, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(table), hscale3, 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);


	/* Add a border to the vbox and put it in the frame */
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
	gtk_container_add(GTK_CONTAINER(frame), vbox);

	/* Okay and cancel buttons */
/*	buttonbox = gtk_hbox_new(TRUE, 5);

	autoadj = button = image_button_new(audiobox, IMAGE_BUTTON_TYPE_TOGGLE, "Auto Adjust", speaker_xpm);
	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(audio_adjust), NULL);
	gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, TRUE, 0);

	button = image_button_new(audiobox, IMAGE_BUTTON_TYPE_BUTTON, "Ok", ok_xpm);
	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(audio_okay), NULL);
	gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, TRUE, 0);

	button = image_button_new(audiobox, IMAGE_BUTTON_TYPE_BUTTON, "Cancel", cancel_xpm);
	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(audio_cancel), NULL);
	gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, TRUE, 0);
*/	
	gtk_box_pack_start(GTK_BOX(outerbox), frame, FALSE, FALSE, 5);


	/* The auto adjustment stuff */	
	aaframe = gtk_frame_new("Auto Adjustment");
	abox = gtk_vbox_new(FALSE, 5);
	gtk_container_set_border_width(GTK_CONTAINER(abox), 10);
	
	instruct = gtk_label_new(INSTRUCTIONS);
	gtk_label_set_justify(GTK_LABEL(instruct), GTK_JUSTIFY_FILL);
	level = gtk_progress_bar_new();

	gtk_widget_set_sensitive(GTK_WIDGET(instruct), FALSE);
	gtk_widget_set_sensitive(GTK_WIDGET(level), FALSE);

	gtk_box_pack_start(GTK_BOX(abox), instruct, FALSE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(abox), level, FALSE, FALSE, 5);
	gtk_container_add(GTK_CONTAINER(aaframe), abox);

	gtk_box_pack_start(GTK_BOX(outerbox), aaframe, FALSE, FALSE, 5);
	gtk_widget_set_sensitive(GTK_WIDGET(aaframe), FALSE);



/*	gtk_box_pack_end(GTK_BOX(outerbox), buttonbox, FALSE, FALSE, 5);*/
	gtk_container_set_border_width(GTK_CONTAINER(outerbox), 10);

	/*gtk_container_add(GTK_CONTAINER(audiobox), outerbox);
	gtk_box_pack_start(GTK_BOX(audiobox), outerbox, TRUE, TRUE, 5);*/

	open_mixerfd();
	return 0;
}

static int show_option_screen(void)
{
	gtk_widget_show_all(audiobox);
	gtk_grab_add(audiobox);
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), mainvol>>8);
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj2), micvol>>8);

	while(audiobox && !gtk_main_iteration_do(TRUE));
	return 0;
}

int show_audio_control(void)
{
	build_option_screen();
	show_option_screen();
	return 0;
}

static void open_mixerfd(void)
{
	if(!mixer_fd)
		if ( (mixer_fd = open("/dev/mixer", O_RDWR, 0)) < 0)
			perror("/dev/mixer");
	if(ioctl(mixer_fd, SOUND_MIXER_READ_VOLUME, &mainvol) == -1)
			perror("SOUND_MIXER_READ_VOLUME");
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), mainvol>>8);

	if(ioctl(mixer_fd, SOUND_MIXER_READ_MIC, &micvol) == -1)
			perror("SOUND_MIXER_READ_MIC");
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj2), micvol>>8);

	if(ioctl(mixer_fd, SOUND_MIXER_READ_RECLEV, &recvol) == -1)
			perror("SOUND_MIXER_READ_RECLEV");
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj3), recvol>>8);
}

int set_rec_source(void)
{
	int src = SOUND_MASK_MIC;
	if(!mixer_fd)
		if ( (mixer_fd = open("/dev/mixer", O_RDWR, 0)) < 0)
			perror("/dev/mixer");
	if (mixer_fd)
		if (ioctl(mixer_fd, SOUND_MIXER_WRITE_RECSRC, &src) == -1)
			perror("SOUND_MIXER_RECSRC");
	return 0;
}


static void update_mixer_mainvol(GtkAdjustment *adj)
{
	mainvol = (int)adj->value;

	mainvol = ((mainvol << 8)|mainvol);

	if(mixer_fd)
		if(ioctl(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &mainvol) == -1)
			perror("SOUND_MIXER_WRITE_VOLUME");
}

static void update_mixer_micvol(GtkAdjustment *adj)
{
	micvol = (int)adj->value;

	micvol = ((micvol << 8)|micvol);

	if(mixer_fd)
		if(ioctl(mixer_fd, SOUND_MIXER_WRITE_MIC, &micvol) == -1)
			perror("SOUND_MIXER_WRITE_MIC");
}

static void update_mixer_recvol(GtkAdjustment *adj)
{
	recvol = (int)adj->value;

	recvol = ((recvol << 8)|recvol);

	if(mixer_fd)
		if(ioctl(mixer_fd, SOUND_MIXER_WRITE_RECLEV, &recvol) == -1)
			perror("SOUND_MIXER_WRITE_RECLEV");
}
