#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <math.h>
#include <sys/sysinfo.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include "looper.h"
#include "../../common/jackmixer.h"
#include "configfile.h"
#include "sfpreview.h"
#include "../../common/speciallist.h"
#include "../../common/clickmodes.h"
#include "../../common/alphatrack.h"
#include "alphatrack_callback.h"

#ifdef HAVE_ALSA
#include "../../common/midi.h"
#include "../../common/xsession.h"
#endif

#ifdef HAVE_OSC
#include "../../common/osc.h"
#endif


#define DIR_MODE (S_IRWXU|S_IRGRP)
#define DEFAULT_IDLETIME 80

/* global widgets: */
GtkWidget *globalbox;
GtkWidget *footertable;
GtkWidget *footerbox;
GtkWidget *newlooperbutton;
GtkWidget *newbufferbutton;
GtkWidget *suspend_playingbutton;
GtkWidget *suspend_recordingbutton;
GtkWidget *newemptybufferbutton;
GtkWidget *savebutton;
GtkWidget *openbutton;
GtkWidget *status;
GtkWidget *pbar;
GtkWidget *menu;
GtkWidget *menu_bar;
GtkWidget *file_menu;
GtkWidget *help_menu;
GtkWidget *edit_menu;
GtkWidget *load_menu = NULL;
GtkWidget *menu_items;
GtkWidget *waitdialog;
GtkWidget *warndialog;
GtkWidget *window;

/* global data */
speciallist_t *buffer_list;
alphatrack_t *alphatrack = NULL;

int emptybuffercounter = 0;
configfile_handler_t *ch;
int loadfilestate = 0;
int freewheelstate = 0;
int alive = 1;
int countdown = 0;
guint idle_id = 0;

guint context = 0;
guint status_id = 0;
long laststatus = 0;

char *lastpath = NULL;
char *lastconfigfilepath = NULL;
char *projectfilename = NULL;
char *txt = NULL;
/* buffer_list_t *buffer_list; */

looper_list_t *looperlist;
speciallist_t *looperdatalist;
speciallist_t *clickmode_list;

int looperindexcounter = 0;
int bufferindexcounter = 0;

long last_pbar_action = 0;

double lastuptime = 0.0;
double lastidle = 0.0;
float lastload = 0.0;

long lastxruns = 0;

jack_info_t *jack_info;

#ifdef HAVE_ALSA
midi_info_t *midi_info;
xsession_info_t *xsession_info;
#endif

#ifdef HAVE_OSC
osc_info_t *osc_info;
#endif

/*------- EVENT FUNCTIONS -------*/
void set_status (const char* message){
	char* mws;

	laststatus = time(NULL);
	if (status_id) gtk_statusbar_pop ((GtkStatusbar*)status,status_id);
	status_id = 0;
	if (message) {
		mws = malloc (sizeof(char) * (strlen(message) + 3));
		sprintf (mws," %s ",message);
		status_id = gtk_statusbar_push((GtkStatusbar*)status,context,(gchar*)mws);
		free(mws);
	}else{
		status_id = gtk_statusbar_push((GtkStatusbar*)status,context,"  ");
	}
}

void destroy(GtkWidget *widget, gpointer data){
	GtkWidget *dialog;
        gint response;

        dialog = gtk_message_dialog_new (GTK_WINDOW(window),
                                GTK_DIALOG_DESTROY_WITH_PARENT,
                                GTK_MESSAGE_QUESTION,
                                GTK_BUTTONS_YES_NO,
                                "are you sure you want to quit?\n");
	gtk_dialog_add_button (GTK_DIALOG (dialog), "Maybe",1783);
        response = gtk_dialog_run (GTK_DIALOG (dialog));

        gtk_widget_destroy (dialog);
        if (response == GTK_RESPONSE_YES){
		mixer_close(jack_info);
		gtk_main_quit();
        }else if (response == 1783){
		int i = (int)(3.0*rand()/(RAND_MAX+1.0));
/*		printf ("i:%d\n",i);*/
		if (i == 1) gtk_main_quit();
		else set_status("maybe not quitting");
	}else set_status("not quitting");
}       

static gboolean delete_event( GtkWidget *widget,
                              GdkEvent  *event,
                              gpointer   data ){
    	/* Change TRUE to FALSE and the main window will be destroyed with
     	 a "delete_event". */

        destroy(GTK_WIDGET(widget),data);

        return TRUE;
}


static void close_all_loopers (){
	GList *tmpptr = g_list_first (looperlist->listptr);
	looper_gui_t *looper = NULL;

	while (tmpptr){
		if ((looper = (looper_gui_t*)tmpptr->data) != NULL){
			speciallist_delete(looperdatalist, looper->data);
			g_mutex_lock (looperlist->listmutex);
			looper_list_remove_looper(looperlist, looper);
			g_mutex_unlock (looperlist->listmutex);
			looper_close(looper);
			tmpptr = g_list_first (looperlist->listptr);
		}else
			tmpptr = g_list_next(tmpptr);
	}
}

static void close_all_buffers(){
	buffer_info_t *buf = speciallist_get_first(buffer_list);

	while (buf){
		speciallist_delete(buffer_list,buf);
		buffer_delete(buf);
		buf = speciallist_get_first(buffer_list);
	}
}

static void update_bufferlists(){
	GList *tmpptr = NULL;
	looper_gui_t *looper = NULL;
	
	tmpptr = g_list_first (looperlist->listptr);
	
/*	printf ("update bufferlist\n");*/
	while (tmpptr){
		looper = (looper_gui_t*)tmpptr->data;
		if (looper != NULL){
			/*should do some cleanup here maybe */
			if (!looper->id){
/*				printf ("looper removed from list\n");*/
				speciallist_delete(looperdatalist, looper->data);
				g_mutex_lock(looperlist->listmutex);
				looper_list_remove_looper(looperlist, looper);
				g_mutex_unlock(looperlist->listmutex);
				tmpptr = looperlist->listptr;
			}else looper_update_bufferlist (looper,buffer_list);
		}
		tmpptr = g_list_next(tmpptr);
	}
	return;
}

void update_cursorpositions(){
	GList *tmpptr = NULL;
	looper_gui_t *looper = NULL;

	tmpptr = g_list_first (looperlist->listptr);
	while (tmpptr){
		looper = (looper_gui_t*)tmpptr->data;
		if (looper != NULL){
			/*should do some cleanup here maybe */
			if (!looper->id){
/*				printf ("looper removed from list\n");*/
				speciallist_delete(looperdatalist, looper->data);
				looper_list_remove_looper(looperlist, looper);
				tmpptr = looperlist->listptr;
			}
			else looper_update_cursorposition (looper);
		}
		tmpptr = g_list_next(tmpptr);
	}
}

void get_load (){
	FILE *ufile;
	int ok = 0;
	long ut, id;
	int utk, idk;
	double uptime = 0;
	double idle = 0;

	ufile = fopen("/proc/uptime","r");
	if (ufile){
		ok = fscanf(ufile,"%ld.%d %ld.%d",&ut,&utk,&id,&idk);
		if (ok == 4){
			uptime = ut + utk * .01;
			idle = id + idk * .01;
			lastload = 1.0 - (float)(idle - lastidle) / (float)(uptime - lastuptime) ;
/*			printf ("up:%.2lf, %.2lf, idle:%.2lf, %.2lf, lastload:%.2lf ",
				uptime,lastuptime,idle,lastidle,lastload);*/
			lastuptime = uptime;
			lastidle = idle;	
/*			if (!(0.0 < lastload <= 1.0)) lastload = 0.0;*/
			if (!((lastload > 0.0) && ((lastload < 1.0) || (lastload == 1.0)))) lastload = 0.0;
/*			printf ("lastload after:%.2lf\n",lastload);*/
		}
		fclose(ufile);	
	}
}

/* ------------- dialog functions --------------*/
static gint confirm_error (GtkWidget *parent, char *fmt, ...) {
	va_list args;
	gint response = 0;
	GtkWidget *edialog;
	char *message = malloc(256 * sizeof(char));

	va_start(args,fmt);
	vsnprintf(message,255,fmt,args);
        va_end(args);


	edialog = gtk_message_dialog_new (GTK_WINDOW(parent),
                                GTK_DIALOG_DESTROY_WITH_PARENT,
                                GTK_MESSAGE_ERROR,
                                GTK_BUTTONS_OK,
				message);
	gtk_widget_show(GTK_WIDGET(edialog));

	g_signal_connect_swapped (edialog,
                             "response", 
                             G_CALLBACK (gtk_widget_destroy),
                             edialog);
	free(message);
	return response;
}

static gint confirm_overwrite (GtkWidget *parent, const gchar *filename){
        GtkWidget *dialog;
        gint response;  
                        
        dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
                        GTK_DIALOG_DESTROY_WITH_PARENT,
                        GTK_MESSAGE_QUESTION,
                        GTK_BUTTONS_NONE,
                        "\"%s\" already exists.  Do you want to replace it ?",
                        filename);
        gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                        GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
                        "_Replace", GTK_RESPONSE_ACCEPT,
                        NULL);
        
        response = gtk_dialog_run (GTK_DIALOG (dialog));
        
        gtk_widget_destroy (dialog);
   
        return response;
}     

/* main loop function: */

gint main_idlefunc(gpointer data){
	buffer_info_t *buf;
	buffer_info_t *tbuf;
	GList *tmpptr = NULL;
	looper_gui_t *looper;
	gboolean looper_dirty = FALSE;
	int not_ready = 0;
	int ti;
	long thetime = time(NULL);
	
	/* go through buffers and look for changes if buffermutex is available*/
	buf = speciallist_get_first(buffer_list);

	if (alive && !jack_info->alive){
		alive = jack_info->alive;
		confirm_error(window,"jack kicked us out.\n please save and restart.\n");
	}

	if (!alive) set_status ("no jack - no playback !");

	while (buf){
		if (buf->pending == BUFFER_STATUS_READY){
			gtk_progress_bar_update ((GtkProgressBar*)pbar,buf->percent_filled);
/*			printf ("%s... bufferloading finished\n",buf->shortname);*/
			/* this would be the place for size and mono/stereo in shortname */
			set_status ("buffer ready");
			last_pbar_action = thetime;
			looper_dirty = TRUE;
		}

		if (buf->pending && (buf->pending != buf->status)){
			buffer_lock(buf);
			buf->status = buf->pending;
		       	buf->pending = 0;
			buffer_unlock(buf);
		}

		if (buf->status == BUFFER_STATUS_LOADING){
		       	gtk_progress_bar_update ((GtkProgressBar*)pbar,buf->percent_filled);
			gtk_progress_bar_set_text ((GtkProgressBar*)pbar,"buffer loading");
		       	last_pbar_action = thetime;
			set_status ("loading file");
	       	}    

		if (buf->status == BUFFER_STATUS_DEAD){
			/* this should only happen when loading fails*/	
			set_status ("loading failed!");

                        if (buf->error == BUFFER_ERROR_ALLOC)
                               	confirm_error (window, "loading of %s failed \nnot enough memory",buf->shortname);
                       	else if (buf->error == BUFFER_ERROR_NOSF)
                               	confirm_error (window, "could not read soundfile %s\n",buf->shortname);
			else confirm_error (window, "could not load soundfile %s",buf->filename);

			tbuf =  speciallist_get_next(buffer_list,buf);
			speciallist_delete(buffer_list,buf);
			buf = tbuf;
			/* LATER: clean up buf */
		} else {
			if (!(buf->status == BUFFER_STATUS_READY)) not_ready++;
			buf = speciallist_get_next(buffer_list,buf);
		}
	}

	if (countdown) countdown--;	
	else if (loadfilestate == 1){
/*                gint x,y,w,h;*/

/*		printf ("stage 1\n");*/
/*
                config_get_geometry(ch, &x,&y,&w,&h);
                gtk_window_resize((GtkWindow*)window,w,h);
                gtk_window_move((GtkWindow*)window,x,y);
*/

		while ((ti = config_next_buffer(ch)) != 0){
			if (bufferindexcounter < ti) bufferindexcounter = ti;
		       	buf = buffer_new(ti,jack_info->samplerate);
			config_read_buffer (ch,buf);
/*			printf ("buffer nr:%d created\n",ti);*/
			if (buffer_get_status(buf) != BUFFER_STATUS_DEAD)
				speciallist_append(buffer_list,buf);
			else confirm_error(window,"could not load %s",buf->filename);
		}

		buf = speciallist_get_first(buffer_list);
		while (buf){
			buf = speciallist_get_next(buffer_list,buf);
		}

		loadfilestate++;
	}else if (!not_ready && (loadfilestate == 2)){
		while ((ti = config_next_looper(ch)) != 0){
/*			printf ("new looper:%d\n",ti);*/
			if (looperindexcounter < ti) looperindexcounter = ti;
			looper = looper_new(window, globalbox, alphatrack, looperdatalist, clickmode_list, ti, 
					config_get_looper_minimized (ch, ti));
			if (jack_info->samplerate)
				looper_set_samplerate(looper,jack_info->samplerate);
			looper_list_append_looper(looperlist, looper);
			speciallist_append(looperdatalist, looper->data);
			gtk_box_pack_start (GTK_BOX (globalbox), looper->mainbox, FALSE, FALSE, 0);
			looper_update_bufferlist(looper,buffer_list);
		}
/*		printf ("stage 2\n");*/
		loadfilestate++;
		countdown = 5; /* wait a while to settle widgets */
	}else if (loadfilestate == 3){
		gint x,y,w,h;
/*		printf ("stage 3\n");*/

		tmpptr = g_list_first (looperlist->listptr);
		while (tmpptr){
			looper = (looper_gui_t*)tmpptr->data;
			if (looper != NULL)
				if (looper->id)
					config_read_looper (ch,looper);
			tmpptr = g_list_next(tmpptr);
		}
		tmpptr = g_list_first (looperlist->listptr);
                /* do it twice to get the order of loopers right: */
		while (tmpptr){
                        looper = (looper_gui_t*)tmpptr->data;
                        if (looper != NULL)
                                if (looper->id)
                                        config_read_looper (ch,looper);
                        tmpptr = g_list_next(tmpptr);
                }
		config_get_geometry(ch, &x,&y,&w,&h);
                gtk_window_move((GtkWindow*)window,x,y);
                gtk_window_resize((GtkWindow*)window,w,h);
/*                printf ("w:%d, h:%d, x:%d, y:%d\n",w,h,x,y);*/
		gtk_widget_destroy (waitdialog);
                loadfilestate = 0;
		set_status("project loaded");

		close_config_reader(ch);
	}

	/* reset statusbar if inactive for 2+ seconds */
	if ((thetime - last_pbar_action) > 1){
		last_pbar_action = thetime;

		/* change idle scheduling to relax at high loads*/
		get_load();
/*		timeout version: */
		if (idle_id) g_source_remove (idle_id);
        	if (lastload > .4) idle_id =  g_timeout_add((guint)(lastload * 100),(GSourceFunc)main_idlefunc,NULL);
        	else idle_id =  g_timeout_add(DEFAULT_IDLETIME,(GSourceFunc)main_idlefunc,NULL);

		txt = realloc(txt,sizeof(char) * 256);
		memset (txt,0,255);
		snprintf(txt,255,"cpu load:%.1f%%",(float)(lastload * 100.));
		gtk_progress_bar_set_text ((GtkProgressBar*)pbar,txt);
		gtk_progress_bar_update ((GtkProgressBar*)pbar,lastload);
	}

	if (jack_info->freewheeling != freewheelstate){
		freewheelstate = jack_info->freewheeling;
		if (freewheelstate) set_status("entering free wheeling mode");
		else set_status("leaving free wheeling mode");
	}	
	if (jack_info->xruns != lastxruns){
		lastxruns = jack_info->xruns;
		txt = realloc(txt,sizeof(char) * 256);
		memset (txt,0,255);
                snprintf(txt,255,"XRUN! [%ld xruns total]",lastxruns);
		set_status (txt);
	}

	if (looper_dirty) 
		update_bufferlists();

	update_cursorpositions();

	if ((thetime - laststatus) > 10) set_status(NULL);

/*	g_usleep(25000);*/
	g_thread_yield();

	return 1;
}

/*-------GUI CREATE FUNCTIONS----------*/

static char* loadbufferdialog(char* filename){
	GtkWidget *gfc;
        GtkWidget *sfp;
        struct stat     fstat;

        gfc =  gtk_file_chooser_dialog_new("select a soundfile for the new buffer",
                        GTK_WINDOW(window),
                        GTK_FILE_CHOOSER_ACTION_OPEN,
                        GTK_STOCK_CANCEL,
                        GTK_RESPONSE_CANCEL,
                        GTK_STOCK_OPEN,
                        GTK_RESPONSE_ACCEPT,
                                        NULL);
        sfp = sfpreview_new();
        g_signal_connect (G_OBJECT (gfc), "update-preview",
                        G_CALLBACK (sfpreview_update), (gpointer) sfp);
        gtk_file_chooser_set_use_preview_label(GTK_FILE_CHOOSER (gfc),FALSE);
        gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER (gfc),GTK_WIDGET(sfp));
        gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER (gfc),TRUE);
        if (lastpath) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (gfc),lastpath);

        /* LATER: add a preview-function to listen to files in advance, and a GtkFileFilter */

        if (gtk_dialog_run (GTK_DIALOG (gfc)) == GTK_RESPONSE_ACCEPT){
                filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (gfc));
		
                if (stat (filename, &fstat)){
                        printf ("file not found!\n");
			if(filename) free(filename);
			filename = NULL;
                }
        }
        gtk_widget_destroy (sfp);
        gtk_widget_destroy (gfc);

	return filename;
}

static void load_buffer(gpointer data){
	char *filename = NULL;
	buffer_info_t *buf = NULL;
        char* pathend = NULL;

	filename = loadbufferdialog (filename);
	if (filename){
		pathend = strrchr (filename, '/');
		if (pathend){
			lastpath = realloc (lastpath,sizeof(char) * (pathend - filename + 2));
			strncpy (lastpath, filename, (pathend - filename + 1));
			strncpy ((char*)(lastpath + (long)(pathend - filename + 1)),"\0",1);
		}
		buf = buffer_new(++bufferindexcounter,jack_info->samplerate);
		if (jack_info->samplerate){	
			buffer_loadfile(buf,(char*)filename,jack_info->samplerate);
		}
		speciallist_append(buffer_list,buf);	
	} else set_status("loading failed");
	g_free (filename);
}

static void open_discstream(gpointer data){
        char *filename = NULL; 
	buffer_info_t *buf = NULL;
        char* pathend = NULL;

        filename = loadbufferdialog (filename);
        if (filename){
                pathend = strrchr (filename, '/');
                if (pathend){
                        lastpath = realloc (lastpath,sizeof(char) * (pathend - filename + 2));
                        strncpy (lastpath, filename, (pathend - filename + 1));
                        strncpy ((char*)(lastpath + (long)(pathend - filename + 1)),"\0",1);
                }       
                buf = buffer_new(++bufferindexcounter,jack_info->samplerate);
                if (jack_info->samplerate){
                        buffer_open_discstream(buf,(char*)filename,jack_info->samplerate);
		}
                speciallist_append(buffer_list,buf);    
        } else set_status("loading failed");
        g_free (filename);
}

static void suspend_playing(gpointer data){
	GtkWidget* suspend_playingbutton = (GtkWidget*)data;

	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(suspend_playingbutton)))
		jack_info->suspend_playing = 1;
	else 	jack_info->suspend_playing = 0;
}

static void suspend_recording(gpointer data){
        GtkWidget* suspend_recordingbutton = (GtkWidget*)data;

        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(suspend_recordingbutton)))
                jack_info->suspend_recording = 1;
        else    jack_info->suspend_recording = 0;
}


static void open_project (char *filename){
	char* pathend = NULL;
	struct stat     fstat;
	
	if (stat (filename, &fstat))
		 printf ("file not found!\n");
	else{
		pathend = strrchr (filename, '/');
		if (pathend){
			lastconfigfilepath = realloc (lastconfigfilepath,sizeof(char) * (pathend - filename + 2));
			strncpy (lastconfigfilepath, filename, (pathend - filename + 1));
			strncpy ((char*)(lastconfigfilepath + (long)(pathend - filename + 1)),"\0",1);
		}

		if ((ch = open_config_reader(filename,jack_info->samplerate)) != NULL){
			projectfilename = realloc (projectfilename, sizeof(char) * (strlen(filename) +1));
			strcpy (projectfilename,filename);

			waitdialog = gtk_message_dialog_new (GTK_WINDOW (window),
				GTK_DIALOG_DESTROY_WITH_PARENT,
				GTK_MESSAGE_INFO,
				GTK_BUTTONS_NONE,
				"please wait while loading \n \"%s\"",
				filename);

			gtk_widget_show_all (waitdialog);
			gtk_window_set_transient_for(GTK_WINDOW (waitdialog),GTK_WINDOW (window));
	
			txt = realloc(txt,sizeof(char) * 256);
			memset(txt,0,255);
        	        snprintf(txt,255,"%s : %s",jack_info->name, filename);
			gtk_window_set_title(GTK_WINDOW(window),(gchar*)txt);

			close_all_loopers();
			close_all_buffers();

			looperindexcounter = 0;
			bufferindexcounter = 0;

			loadfilestate = 1;
			countdown = 5;
		} else {
/*
			warndialog = gtk_message_dialog_new (GTK_WINDOW (window),
				GTK_DIALOG_MODAL,
				GTK_MESSAGE_ERROR,
				GTK_BUTTONS_CLOSE,
				"Error loading file '%s'",
				filename);
			gtk_dialog_run (GTK_DIALOG (warndialog));
 			gtk_widget_destroy (warndialog);
			printf ("could not read config file!\n");
*/
			set_status("loading failed");
		}
	}
}

static void about_callback(gpointer data){
	GtkWidget *dialog;
	/*gint response;  */
                 
        dialog = gtk_message_dialog_new (GTK_WINDOW(window),
                                GTK_DIALOG_DESTROY_WITH_PARENT,
                                GTK_MESSAGE_INFO,
                                GTK_BUTTONS_CLOSE,
                               	"kluppe looper is developed by dieter kovacic\n"
				"http://kluppe.klingt.org\n"
                        	"contact: kluppe" "@" "klingt.org.\n");
        /*response = */ gtk_dialog_run (GTK_DIALOG (dialog));
                        
        gtk_widget_destroy (dialog);
}

static void open_project_menucallback(gpointer data){
	GtkWidget *gfc;	

	gfc =  gtk_file_chooser_dialog_new("select a projectfile to be loaded",
			GTK_WINDOW(window),
			GTK_FILE_CHOOSER_ACTION_OPEN,
			GTK_STOCK_CANCEL, 
			GTK_RESPONSE_CANCEL,
			GTK_STOCK_OPEN, 
			GTK_RESPONSE_ACCEPT,
					NULL);
	gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER (gfc),TRUE);
	if (lastconfigfilepath) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (gfc),lastconfigfilepath);

	if (gtk_dialog_run (GTK_DIALOG (gfc)) == GTK_RESPONSE_ACCEPT){
		char *filename;
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (gfc));
		open_project (filename);
		g_free (filename);
	}
	gtk_widget_destroy (gfc);
}

static void new_project(){
        close_all_loopers();
        close_all_buffers();
        looperindexcounter = 0;
        bufferindexcounter = 0;
        if (projectfilename){
                free(projectfilename);
                projectfilename = NULL;
        }
}

static void new_project_menucallback(gpointer data){
	GtkWidget *dialog;
        gint response;

        dialog = gtk_message_dialog_new (GTK_WINDOW(window),
                                GTK_DIALOG_DESTROY_WITH_PARENT,
                                GTK_MESSAGE_QUESTION,
				GTK_BUTTONS_YES_NO,
                                "do you really want to close\n"
                                "this project and remove all buffers?\n");
        response = gtk_dialog_run (GTK_DIALOG (dialog));
                        
        gtk_widget_destroy (dialog);
	if (response == GTK_RESPONSE_YES){
		new_project();
		set_status("project colsed");
	}else set_status("not coling project");
	
}

static void save_project_as_menucallback(gpointer data){
	GtkWidget *gfc;
	char* pathend = NULL;
	struct stat fstat;
	int  ret;
	gint x,y,w,h;
       	gint response = 0;
 
	gfc =  gtk_file_chooser_dialog_new("select a projectfilename for saving",
			GTK_WINDOW(window),
			GTK_FILE_CHOOSER_ACTION_SAVE,
			GTK_STOCK_CANCEL,
			GTK_RESPONSE_CANCEL,
			GTK_STOCK_SAVE,
			GTK_RESPONSE_ACCEPT,
					NULL);
	gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER (gfc),TRUE);
	if (lastconfigfilepath) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (gfc),lastconfigfilepath);

	if (gtk_dialog_run (GTK_DIALOG (gfc)) == GTK_RESPONSE_ACCEPT){
		char *filename;
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (gfc));
		filename = config_check_filename(filename);

		response = GTK_RESPONSE_ACCEPT;

		if (!stat (filename, &fstat))
			response = confirm_overwrite(window,(gchar*)filename);
		if (response == GTK_RESPONSE_ACCEPT){

			/* gtk_window_get_position () to save position too ??? */
			gtk_window_get_size(GTK_WINDOW (window),&w,&h);
			gtk_window_get_position(GTK_WINDOW (window),&x,&y);
			ret = save_config (looperlist->listptr, buffer_list, lastpath, lastconfigfilepath, filename,x,y,w,h,jack_info->samplerate);
			if (ret < 0)	set_status("saving failed !");
			else {
				projectfilename = realloc (projectfilename, sizeof(char) * (strlen(filename) +1));
				strcpy (projectfilename,filename);
				printf ("filename:%s\n",projectfilename);
				set_status ("project saved");
				snprintf(txt,255,"%s : %s",jack_info->name, filename);
                        	gtk_window_set_title(GTK_WINDOW(window),(gchar*)txt);
			}
	
			pathend = strrchr (filename, '/');
			if (pathend){
				lastconfigfilepath = realloc (lastconfigfilepath,sizeof(char) * (pathend - filename + 2));
				strncpy (lastconfigfilepath, filename, (pathend - filename + 1));
				strncpy ((char*)(lastconfigfilepath + (long)(pathend - filename + 1)),"\0",1);
			}
		}
		g_free (filename);
	}
	gtk_widget_destroy (gfc);
}

static void save_project_menucallback(gpointer data){
	int ret = 0;
	gint x,y,w,h;

	gtk_window_get_size(GTK_WINDOW (window),&w,&h);
	gtk_window_get_position(GTK_WINDOW (window),&x,&y);
	if (projectfilename)
		ret = save_config (looperlist->listptr, buffer_list, lastpath, lastconfigfilepath, projectfilename,x,y,w,h,jack_info->samplerate);
	else save_project_as_menucallback(data);

	if (ret < 0)  set_status("saving failed !");

}

static void create_empty_buffer(gpointer data){
	GtkWidget *dialog, *label1, *label2, *label3, *nameline ,*stereo, *spin;
	GtkAdjustment *adj;
/*	gint result;*/
	buffer_info_t *buf;
/*	char* shortname = malloc (sizeof(char) * 255);*/

	label1 = gtk_label_new ("\ncreate a new buffer with");
	adj = (GtkAdjustment *) gtk_adjustment_new (10.0, 0.0, 1000.0, 1, .1, 2);
	spin = gtk_spin_button_new (adj, 1, 4);
	stereo = gtk_check_button_new_with_label ("stereo");
	txt = realloc(txt,sizeof(char) * 256);
	memset(txt,0,sizeof(char) * 255);
	sprintf (txt,"MB: buffer nr. %d",++bufferindexcounter);
	nameline = gtk_entry_new();
	gtk_entry_set_max_length((GtkEntry*)nameline,255);
	gtk_entry_set_text((GtkEntry*)nameline,(gchar*)txt);
	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(stereo),TRUE);

	label2 = gtk_label_new ("seconds size.\n");
	label3 = gtk_label_new ("name:");

	dialog = gtk_dialog_new_with_buttons ("create empty buffer",
			(GtkWindow*)window,
			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_STOCK_OK,
			GTK_RESPONSE_ACCEPT,
			GTK_STOCK_CANCEL,
			GTK_RESPONSE_REJECT,
			NULL);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),label1);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),spin);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),label2);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),stereo);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),label3);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),nameline);

	gtk_widget_show_all (dialog);

	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT){
		buf = buffer_new(bufferindexcounter,jack_info->samplerate);
		buffer_lock(buf);
		buf->pending = BUFFER_STATUS_LOADING;
		if (jack_info->samplerate)
			buffer_resize (buf,
				(long)(gtk_spin_button_get_value ((GtkSpinButton*)spin) * jack_info->samplerate));
/*		printf ("i'm here:%ld\n",buffer_get_size(buf));*/
		if (buffer_get_size(buf)){
			buffer_set_shortname(buf,(char*)gtk_entry_get_text((GtkEntry*)nameline));
			buf->pending = BUFFER_STATUS_READY;
		}else{
			buffer_set_shortname(buf,"empty buffer");
			buf->pending = BUFFER_STATUS_DEAD;
		}
		if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stereo)))
                        buffer_set_channels(buf,1);
                else	buffer_set_channels(buf,2);
		buffer_unlock(buf);

		speciallist_append(buffer_list,buf);
	}
	gtk_widget_destroy (dialog);
	set_status ("buffer created");
}
	
static void create_looper(gpointer data){	
	looper_gui_t *loop = looper_new(window, globalbox, alphatrack, looperdatalist, clickmode_list, ++looperindexcounter, 0);
/*	printf ("counter:%d\n",looperindexcounter);*/

	if (jack_info->samplerate)
		looper_set_samplerate(loop,jack_info->samplerate);

	looper_list_append_looper(looperlist, loop);
	speciallist_append(looperdatalist, loop->data);
	
	gtk_box_pack_start (GTK_BOX (globalbox), loop->mainbox, FALSE, FALSE, 0);

	looper_update_bufferlist(loop,buffer_list);
	set_status("new looper created");
}


static void create_mainlayout(){

	/* footer */
	footerbox = gtk_hbox_new (FALSE,3);

	/* http://developer.gnome.org/doc/API/2.0/gtk/GtkUIManager.html */

	newlooperbutton = gtk_button_new_with_label (" new looper ");
	g_signal_connect (G_OBJECT(newlooperbutton), "clicked", G_CALLBACK(create_looper), NULL);
	gtk_box_pack_start (GTK_BOX (footerbox),newlooperbutton,FALSE, FALSE, 0);
	gtk_widget_show (newlooperbutton);

	newbufferbutton = gtk_button_new_with_label (" new buffer from file ");
	g_signal_connect (G_OBJECT(newbufferbutton), "clicked", G_CALLBACK(load_buffer), NULL);
	gtk_box_pack_start (GTK_BOX (footerbox),newbufferbutton,FALSE, FALSE, 0);
	gtk_widget_show (newbufferbutton);

	newbufferbutton = gtk_button_new_with_label (" new discstream ");
        g_signal_connect (G_OBJECT(newbufferbutton), "clicked", G_CALLBACK(open_discstream), NULL);
        gtk_box_pack_start (GTK_BOX (footerbox),newbufferbutton,FALSE, FALSE, 0);
        gtk_widget_show (newbufferbutton);

	newemptybufferbutton  = gtk_button_new_with_label (" new empty buffer ");
	g_signal_connect (G_OBJECT(newemptybufferbutton), "clicked", G_CALLBACK(create_empty_buffer), NULL);
	gtk_box_pack_start (GTK_BOX (footerbox),newemptybufferbutton,FALSE, FALSE, 0);
	gtk_widget_show (newemptybufferbutton);

        suspend_playingbutton = gtk_toggle_button_new_with_label (" suspend playback ");
        g_signal_connect (G_OBJECT(suspend_playingbutton), "toggled", G_CALLBACK(suspend_playing), NULL);
        gtk_box_pack_start (GTK_BOX (footerbox),suspend_playingbutton,FALSE, FALSE, 0);
        gtk_widget_show (suspend_playingbutton);

	suspend_recordingbutton = gtk_toggle_button_new_with_label (" suspend rec. ");
        g_signal_connect (G_OBJECT(suspend_recordingbutton), "toggled", G_CALLBACK(suspend_recording), NULL);
        gtk_box_pack_start (GTK_BOX (footerbox),suspend_recordingbutton,FALSE, FALSE, 0);
        gtk_widget_show (suspend_recordingbutton);

	status = gtk_statusbar_new();
	gtk_statusbar_set_has_resize_grip((GtkStatusbar*)status, TRUE);
	gtk_box_pack_start (GTK_BOX (footerbox),status, TRUE, TRUE, 0);
	gtk_widget_set_size_request (status,200,10); /* was 250,10 */
	gtk_widget_show (status);	
	context = gtk_statusbar_get_context_id ((GtkStatusbar*)status,"mainwindow");

	pbar = gtk_progress_bar_new();
	gtk_box_pack_start (GTK_BOX (footerbox),pbar, TRUE, TRUE, 0);
	gtk_widget_set_size_request (pbar,150,10); /* was 400,10 */
	gtk_widget_show (pbar);

	gtk_widget_show (footerbox);

	/* top menu */
	menu = gtk_menu_new ();

	menu_items = gtk_menu_item_new_with_label ("new empty project");
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
        g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
                                      G_CALLBACK (new_project_menucallback),NULL);
        gtk_widget_show (menu_items);

	menu_items = gtk_menu_item_new_with_label ("open project");
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
	g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
				      G_CALLBACK (open_project_menucallback),NULL);
	gtk_widget_show (menu_items);

	menu_items = gtk_menu_item_new_with_label ("save  project as...");
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
	g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
				      G_CALLBACK (save_project_as_menucallback),NULL);
	gtk_widget_show (menu_items);

	menu_items = gtk_menu_item_new_with_label ("save  project");
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
/*	gtk_widget_set_sensitive (menu_items, FALSE);*/
	g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
				      G_CALLBACK (save_project_menucallback),NULL);
	gtk_widget_show (menu_items);

	menu_items = gtk_separator_menu_item_new();
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
	gtk_widget_show (menu_items);

	menu_items = gtk_menu_item_new_with_label ("quit");
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
	g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
				      G_CALLBACK (destroy), 
				      (gpointer) "quit");
	gtk_widget_show (menu_items);

	file_menu = gtk_menu_item_new_with_label ("File");
	gtk_widget_show (file_menu);
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (file_menu), menu);

	menu_bar = gtk_menu_bar_new ();
	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), file_menu);

	/* edit menu */
        menu = gtk_menu_new ();

        menu_items = gtk_menu_item_new_with_label ("new looper");
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
        g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
                                      G_CALLBACK (create_looper),
                                      NULL);
        gtk_widget_show (menu_items);

        menu_items = gtk_menu_item_new_with_label ("new buffer from file");
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
        g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
                                      G_CALLBACK (load_buffer),
                                      NULL);
        gtk_widget_show (menu_items);

        menu_items = gtk_menu_item_new_with_label ("new discstream");
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
        g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
                                      G_CALLBACK (open_discstream),
                                      NULL);
        gtk_widget_show (menu_items);

        menu_items = gtk_menu_item_new_with_label ("new empty buffer");
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
        g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
                                        G_CALLBACK (create_empty_buffer),
                                        NULL);
        gtk_widget_show (menu_items);

        edit_menu = gtk_menu_item_new_with_label ("Edit");
        gtk_widget_show (edit_menu);
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (edit_menu), menu);

/*
        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
        gtk_widget_show (menu_items);
*/

	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), edit_menu);

	/* help menu */
        menu = gtk_menu_new ();

	menu_items = gtk_menu_item_new_with_label ("about");
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);

        g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
                                      G_CALLBACK (about_callback),
                                      NULL);
        gtk_widget_show (menu_items);

        help_menu = gtk_menu_item_new_with_label ("Help");
        gtk_widget_show (help_menu);
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (help_menu), menu);


	/* finalize: */
        gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), help_menu);

        gtk_widget_show (menu_bar);

	
	/* main area */
	globalbox = gtk_vbox_new (FALSE,0);
	gtk_widget_show (globalbox);
	gtk_box_pack_start (GTK_BOX (globalbox), menu_bar, FALSE, FALSE, 0);
/*	gtk_box_pack_end (GTK_BOX (globalbox), footertable, FALSE, FALSE, 0);*/
	gtk_box_pack_end (GTK_BOX (globalbox), footerbox, FALSE, FALSE, 0);

	set_status("welcome");
}

/*--------MAIN--------*/

int main(int argc, char *argv[])
{
	int retval/*, count = 0*/;
	const char *defaultdir = NULL;
	struct stat fstat;

	defaultdir = g_get_home_dir ();
	defaultdir = g_strconcat (defaultdir,"/.kluppe",NULL);
		
	/* global data initialisation */
	buffer_list = speciallist_new();

	looperdatalist = speciallist_new();

	/* start the loadbuffer thread */
	buffer_discthread_launch(buffer_list);

#ifdef HAVE_ALSA
        /* create midi thread: */
        midi_info = midi_info_new(looperdatalist);
/*
  	xsession somehow interferes with note-off events - check before enabling
	while (!midi_info->seq && (count++ < 10000)) usleep(10);
	xsession_info = xsession_new(looperdatalist, midi_info->seq);
*/
#endif

#ifdef HAVE_OSC
	osc_info = osc_info_new(looperdatalist);
#endif
	
	/* start the jack threads */
        jack_info = mixer_launch(looperdatalist);

        /*g_thread_init(NULL);*/
        /* gdk_threads_init();*/
/*        gdk_threads_enter();*/

	gtk_init(&argc, &argv);

        looperlist = looper_list_new();

	/*alphatrack: */
	alphatrack = alphatrack_new();
        if (alphatrack){
                alphatrack_set_event_callback(alphatrack,alphatrack_event_callback,looperlist);
		alphatrack_write_lcd(alphatrack,"kluppe says hi! ",0,0);
		alphatrack_write_lcd(alphatrack,"                ",0,1);
		alphatrack_set_faderpos(alphatrack,0);
		alphatrack_clear_leds(alphatrack);
	}
	

	/* create main gui elements */
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title (GTK_WINDOW (window), jack_info->name);
	g_signal_connect(window, "destroy",G_CALLBACK(destroy), NULL);
	g_signal_connect (G_OBJECT (window), "delete_event",G_CALLBACK (delete_event), NULL);
	/* gtk_container_set_border_width(GTK_CONTAINER (window), 10);*/

	create_mainlayout();
	
	gtk_container_add(GTK_CONTAINER(window), globalbox);

	/* load the icon for the window; here we just load one, default icon */

     	gtk_window_set_default_icon_from_file (PIXMAPS_DIR "/kluppe.png",NULL);
/*	gtk_window_set_icon (GTK_WINDOW (window), gdk_pixbuf_new_from_file ("kluppe.png",NULL));*/


	gtk_widget_show (window);

	if (!jack_info->samplerate){
                confirm_error(window,"could not connect to jack!\n no playback/recording possible.");           
		jack_info->samplerate = 44100;
	}
	
	if (!jack_info->alive) alive = 0;

	clickmode_list = clickmodes_create(jack_info->samplerate);

	/* idle callback looks for changes in buffers and loopers */		
	/* timeout version: */
	idle_id = g_timeout_add(DEFAULT_IDLETIME,(GSourceFunc)main_idlefunc,NULL);

	/* idle version (eats a lot of cpu */
/*	gtk_idle_add_priority(G_PRIORITY_LOW,main_idlefunc,NULL);*/

	retval = mkdir(defaultdir, DIR_MODE);
	if (retval)
		if (errno != EEXIST)
			printf ("%s %s\n",defaultdir,strerror(errno));

	if (!stat(defaultdir, &fstat)) {	
		lastconfigfilepath = malloc (sizeof(char) * (strlen(defaultdir) + 1));
		strcpy (lastconfigfilepath, defaultdir);
	}

	if (argc > 1) open_project(argv[1]);

	gtk_main();
/*	gdk_threads_leave();*/

	mixer_close(jack_info);

	printf ("bye\n");

  	return 0;
}
