/* main function and some of the most important tools */
#define MAIN_C

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>
#include "gp_getopt.h"
#include "main.h"
//#include "genpak.h"


opt_s opt ;

/* writes a text into a specified text window or, if window is not currently
 * displayed, into the apripriate buffer */
int write_text(char *text, okno_s *okno, opt_s *o, int color) {

	if(!text) return EXIT_FAILURE;

	gtk_text_insert(GTK_TEXT(okno->tekst), o->czcionka, &o->colors[color], NULL, text, -1) ;
	gtk_text_insert(GTK_TEXT(okno->tekst), o->czcionka, NULL, NULL, "\n", -1) ;
			
	return EXIT_SUCCESS ;
}


/* kills a process. Called from a button widget */
void killer(GtkWidget *kontrolka, gpointer dane) {
	GtkWidget * window ;
	int *pid ;
	data_s *d = (data_s *) dane ;

	pid = (int *) d->data1 ;
	window = d->window1 ;

	say(0, "killing PID %i", *pid) ;

	if(kill(*pid, SIGKILL)) 
		say(0, "Could not kill the process") ;
	else
		say(0, "Process %i KIA", *pid) ;
	
}

/* creates a window with a big red button on it :-) */
data_s* executor_abort(char *comline, int *pid, opt_s *o) {
	GtkWidget * res, *label, *abort, *progress ; 
	char *labeltext ;
	data_s *d ;

	say(0, "creating `abortion box'") ;
	res = gtk_dialog_new() ;

	labeltext = malloc(strlen(comline) + 200) ;
	sprintf(labeltext, "Executing\n%s\n(PID %i)", comline, *pid) ;
	label = gtk_label_new(labeltext) ;

	abort = gtk_button_new_with_label("Abort") ;

	d = malloc(sizeof(*d)) ;
	d->data1 = pid ;
	d->window1 = res ;

  gtk_signal_connect(GTK_OBJECT(abort), "clicked",
	    GTK_SIGNAL_FUNC(killer), d) ;

	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(res)->vbox), label) ;
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(res)->action_area), abort) ;
	GTK_WIDGET_SET_FLAGS(abort, GTK_CAN_DEFAULT) ;
	gtk_widget_grab_default(abort) ;

	gtk_widget_show_all(res) ;
	while(gtk_events_pending()) gtk_main_iteration() ;
	gdk_window_set_cursor(res->window, o->cursor_wait) ;
	gtk_grab_add(res) ;
	free(labeltext) ;
	say(0, "pid now: %i", *((int*) d->data1)) ;
	return d ;
}


/* this one is really responsible for the system() call, but gets called by
 * execute() lower down */
int executor(char *comline, opt_s *o) {
	int pid, exit, exitpid, status = -99 ;
	data_s *terminator ;
	debug = 1 ;

	/* fork so that user can interrupt the process */
	pid = fork() ;

	switch(pid){
		case -1: /* fork failed */
			komunikat("Ops, there was a problem.\n"
				"Actually, I couldn't fork \n"
				"(that is a little more information\n"
				"that you need, I know).") ;
			return EXIT_FAILURE ;
		case 0: /* child process */
			exit = system(comline) ;

			if(WIFEXITED(exit)) {
				exit = WEXITSTATUS(exit) ;
				say(0, "executor: child: exited normally, %i", exit) ;
			} else {
				say(0, "executor: child: exited abnormally, code %i", exit) ;
			}
				
			_exit(exit) ;
			break;
		default: /* parent */
			terminator = executor_abort(comline, &pid, o) ;

			/* refreshing dialog while waiting for the child to exit */
			while( (exitpid = waitpid(pid, &status, WNOHANG)) <= 0) {
				while(gtk_events_pending()) gtk_main_iteration() ;
			}

			gtk_widget_destroy(terminator->window1) ;
			free(terminator) ;
			debug = 0 ;
			return status ;
			break ;
	}
	debug = 0 ;
	return EXIT_SUCCESS ;
}


/* runs the command specified in the comline and adjusts output / input / error
 * windows */
int execute(char *comline, opt_s *o) {

	char *tmp, *real;
	int color = 0, status, exit_value ;
	FILE *fp ;

	if(!comline) return EXIT_FAILURE ;
	gdk_window_set_cursor(o->okno->window, o->cursor_wait) ;

	/* filling in file parameters and executing the command */
	o->command_history = g_list_prepend(o->command_history, comline) ;

	if(window_tofile(&o->panels[0], o) || 
		write_text(comline, &o->panels[2], o, 3)) {
		gdk_window_set_cursor(o->okno->window, o->cursor_normal) ;
		return EXIT_FAILURE ;
	}

	/* creating the real command line to be executed */
	tmp = my_strsubst(comline, "%input", o->panels[0].tmpfil) ;
	real = malloc(strlen(tmp) + 2 * strlen(o->panels[1].tmpfil) + 10) ;
	sprintf(real, "%s > %s 2> %s", tmp, o->panels[1].tmpfil, o->panels[2].tmpfil) ;
	say(0, "executing %s", real) ;
	
	/* OK, let's go */
	status = executor(real, o) ;

	/* we don't need this any more; but we have to keep comline */
	free(real) ;

	gdk_window_set_cursor(o->okno->window, o->cursor_normal) ;

	/* checking whether child exited normally */
	if(!WIFEXITED(status)) {
		say(0, "Child process exited abnormally.") ;
		return EXIT_FAILURE ;
	}

	switch( (exit_value = WEXITSTATUS(status)) ) { 
		case 127:
			komunikat("/bin/sh is missing or program not found") ;
			return EXIT_FAILURE ;
			break ;
		case 0:
			color = 2 ; /* green color */
			break ;
		default:
			komunikat("There was an error executing your command.\n"
				"system() returned %i", exit_value) ;
			write_text("Error during execution\n", &o->panels[2], o, 1) ;
			color = 1 ; /* red */
			break ;
	}

	if( (fp = fopen(o->panels[2].tmpfil, "r")) == NULL) {
		komunikat("Cannot open temporary error file for reading.\n"
		"This probably means serious problems with this program") ;
		return EXIT_FAILURE;
	}

	read_file(fp, o, &o->panels[2], color) ;
	fclose(fp) ;

	if( (fp = fopen(o->panels[1].tmpfil, "r")) == NULL) {
		komunikat("Cannot open temporary output file for reading.\n"
		"This probably means serious problems with this program") ;
		return EXIT_FAILURE;
	}

	read_file(fp, o, &o->panels[1], -1) ;
	fclose(fp) ;
	return EXIT_SUCCESS ;
}


/* on pressing "OK" button, this function takes the string from an entry field */
void command_end_dialog(GtkWidget *okno, gpointer dane) {
	data_s *d ;

	/* cast the arguments into apriopriate types */
	d = (data_s *) dane ;

	/* read the string from the entry button and store it in the provided string */
	d->data = gtk_editable_get_chars(GTK_EDITABLE(d->window1), 0, -1) ;

	/* close the window */
	gtk_widget_destroy(d->window2) ;
	gtk_main_quit() ;

}


/* executes some command */
void command(gpointer dane, guint signal, GtkWidget *kontrolka) {
	char *polec ;
	data_s d ;	/* temporal storage needed for argument passing */
	opt_s *o ;	/* general program options */
	GtkWidget *label, *button, *dialog, *entry ;

	o = (opt_s *) dane ;

	/* Creating dialog window */
	dialog = gtk_dialog_new() ;
	gtk_signal_connect(GTK_OBJECT(dialog), "destroy", 
		GTK_SIGNAL_FUNC(zakoncz_dialog), dialog) ;
	gtk_window_set_title(GTK_WINDOW(dialog), "Optional command") ;
	gtk_container_set_border_width(GTK_CONTAINER(dialog), 10) ;

	/* short description */
	label = gtk_label_new(
		"Please enter a command which should be executed using /bin/sh\n"
		"Use a `%input' as a substitute for a file containing the input window\n"
		"For example, to count the characters in the input window, type in: \n"
		"wc -c %input \n"
		"Do not use any other '%' characters.\n") ;
	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT) ;
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label) ;

	/* entry field */
	entry = gtk_combo_new() ;
	gtk_combo_set_popdown_strings(GTK_COMBO(entry), o->command_history) ;
	gtk_entry_set_max_length(GTK_ENTRY(GTK_COMBO(entry)->entry), 150) ;
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry) ;
	gtk_signal_connect(GTK_OBJECT(GTK_COMBO(entry)->entry), "activate",
		GTK_SIGNAL_FUNC(command_end_dialog), &d) ;

	/* this data is needed by the command_dialog_end function */
	d.data = NULL ;
	d.window1 = GTK_COMBO(entry)->entry ;
	d.window2 = dialog ;

	/* "OK" button */
	button = gtk_button_new_with_label("OK") ;
	gtk_signal_connect(GTK_OBJECT(button), "clicked", 
		GTK_SIGNAL_FUNC(command_end_dialog), &d) ;
	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT) ;
	gtk_widget_grab_default(button) ;
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), button);

	gtk_window_set_focus(GTK_WINDOW(dialog), GTK_COMBO(entry)->entry) ;
	gtk_widget_show_all(dialog) ;

	gtk_main() ;

	polec = (char*) d.data ;

	/* checking whether we left via window destroy or "OK" button */
	if(!polec) {
		printf("No command issued\n") ;
		return ;
	}

	execute(polec, o) ;
	say(0, "command dialog finished") ;
}


/* handling keyboard interrupts */
void signal_handler(int signal) {

	switch(signal){
		case SIGINT:
			say(0, "Keyboard interrupt caught, exiting") ;
			break ;
		case SIGSEGV:
			fprintf(stderr, "\nMemory protection fault (SIGSEGV caught).\n"
			"Please record Arka messages and mail them to january@bioinformatics.org\n\n") ;
			exit(EXIT_FAILURE) ;
			break ;
		default:
			break ;
	}

	write_rcfile(&opt) ;
	exit(EXIT_FAILURE) ;

}


/* parses command line options */
int parse_command_line(int argc, char *argv[]) {
	char c ;
	extern int optind ;


	say(0, "Parsing command line options") ;

	while ((c = gp_getopt(argc, argv, "dvh")) != EOF)
		switch(c) {
		case 'd':
			debug = TRUE ;
			say(0, "Running in debug mode") ;
			break ;
		case 'v':
			fprintf(stderr, "%s, version %s\n", PROGRAM, VERSION) ;
			exit(EXIT_SUCCESS) ;
		case 'h':
			help_short_info(PROGRAM) ;
			exit(EXIT_SUCCESS) ;
		default:
			fprintf(stderr, "%s: wrong option '%c'\n", argv[0], c) ;
			fprintf(stderr, "Type `%s -h' or choose Help/Readme to get help\n", argv[0]) ;
			komunikat("%s: wrong option '%c'\n"
				"Choose Help/Readme to get help", argv[0], c) ;
			break;
		}

	return optind ;
}


/*
 * main function -- short, what?
 */
int main(int argc, char *argv[]) {
	int optind, i ;
	GtkWidget *popup ;

	if(!gtk_init_check(&argc, &argv)) {

		fprintf(stderr, "\nGUI could not be initialized.\n"
			"Maybe you are trying to run Arka from a text terminal?\n"
			"This isn't goint to work, you know.\n\n") ;

		exit(EXIT_FAILURE);
	}

	memory_start() ;

	/* the wee little window shown at the start of the program */
	popup = popup_at_start(&opt) ;
	while(gtk_events_pending()) gtk_main_iteration() ;

	/* initialize the options structure */
	options_initialize(&opt) ;

	/* reading command line options */
	optind = parse_command_line(argc, argv) ;

	/* reading the configuration file to update more options */
	read_rcfile(&opt) ;

	/* setting up the main program window */
	inicjuj_main_window(&opt) ;

	gdk_window_set_cursor(opt.okno->window, opt.cursor_normal) ;

	/* allocating colors */
	allocate_colors(&opt) ;

	/* catching some signals */
	signal(SIGINT, signal_handler) ;
	signal(SIGSEGV, signal_handler) ;

	while(gtk_events_pending()) gtk_main_iteration() ;
	/* read file from command line input */
	if(optind < argc) {
		printf("One left\n") ;
		printf("%s\n", argv[optind] ) ;
		file_open_from_name(&opt, 0, argv[optind]) ;
	}

	say(0, "looking for the next one") ;

	if( (optind + 1) < argc) {
		printf("Still one left\n") ;
		printf("%s\n", argv[optind + 1] ) ;
		opt.panels[1].filenam = my_strdup(argv[optind + 1]) ;
	}

	/* starting the main gtk loop */
	gtk_main() ;

	/* cleaning up - the program is finished! */
	for(i = 0 ; i < 3 ; i++) remove(opt.panels[i].tmpfil) ;

	/* writing the configuration file */
	write_rcfile(&opt) ;

	return EXIT_SUCCESS ;
}

