/*      Get and Resume Elite EDition source code

Get and Resume Elite EDition (GREED)
Copyright (C) 1999  Anoakie Turner
GUI by Bruno Barberi Gnecco

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., 675 Mass Ave, Cambridge, MA 02139, USA.

For more information on the GPL, please go to:
http://www.gnu.org/copyleft/gpl.html


        Contact:  Anoakie Turner
                  Anoakie.Turner@asu.edu

                  13240 N. 94th Pl.
                  Scottsdale, AZ 85260
*/

#include "main.h"

GtkWidget *filelist;

static char *fileStatus[] = { "Waiting", "Downloading", "Hold", 
	"Paused", "Stopped" };

static void GUIInsertUrl (GtkWidget *w, gpointer data)
/*************************************
**	void GUIInsertUrl (GtkWidget *, gpointer)
**
** Wrapper for insertion of download URL.
** Written by Bruno Barberi Gnecco
*************************************/
{	char *newitem[3], rows[4], *enttext;
	int row;
	NODE *curr;

	enttext = gtk_entry_get_text(GTK_ENTRY(data));
	if (enttext == NULL)
	{	message_box("Error", "URL is not valid.\n"
			"You must type http:// or ftp://");
		return;
	}
	gtk_clist_freeze(GTK_CLIST(filelist));
	newitem[1] = strdup(enttext);
	curr = InsertURL(newitem[1], RECURSE);
	if (!Parse(&curr->URL)) 
	{	message_box("Error", "Error parsing URL");
		RemoveURL(newitem[1]);
		return;
	}
	
	newitem[2] = fileStatus[0];
	newitem[0] = NULL;
	row = gtk_clist_append(GTK_CLIST(filelist), newitem);
	sprintf(rows, "%d", row+1);
	gtk_clist_set_text(GTK_CLIST(filelist), row, 0, rows);
	gtk_clist_thaw(GTK_CLIST(filelist));
}

static void GUIRemoveUrl (GtkWidget *w, gpointer data)
/*************************************
**	void GUIRemoveUrl (GtkWidget *, gpointer)
**
** Wrapper for remotion of download URL.
*************************************/
{	char *url, *status, number[4];
	int row =  GPOINTER_TO_INT((GTK_CLIST(filelist)->selection)->data);

	if (GTK_CLIST(filelist)->rows == 0)
		return;
	gtk_clist_freeze(GTK_CLIST(filelist));
	gtk_clist_get_text(GTK_CLIST(filelist), row, 1, &url);
	gtk_clist_get_text(GTK_CLIST(filelist), row, 2, &status);
	if (strcmp(status, fileStatus[0]) || strcmp(status, fileStatus[2])) 
	/* Waiting or hold */
	{	RemoveURL(url);
		gtk_clist_remove(GTK_CLIST(filelist), row);
	}
	else
	{	NODE *cur = list->next;
		while (strcmp(url, cur->URL.name))
			cur = cur->next;
		kill(cur->URL.pid, SIGINT);
		RemoveURL(url);
		gtk_clist_remove(GTK_CLIST(filelist), row);
	}
	for (; row < GTK_CLIST(filelist)->rows; row++)
	{	sprintf(number, "%d", row+1);
		gtk_clist_set_text(GTK_CLIST(filelist), row, 0, number);
	}
	gtk_clist_thaw(GTK_CLIST(filelist));	
}

static void GUIRemoveAllUrl (GtkWidget *w, gpointer data)
/*************************************
**	void GUIRemoveAllUrl (GtkWidget *, gpointer)
**
** Wrapper for insertion of download URL.
*************************************/
{	int really = confirm("Confirm", "Really delete all URLs from list?");
	if (really != OK)
		return;
	while (GTK_CLIST(filelist)->rows > 0)
	{	gtk_clist_select_row(GTK_CLIST(filelist), 0, 0);
		GUIRemoveUrl(w, data);
	}
}

static void GUISwapUrl (GtkWidget *w, gpointer data)
/*************************************
**	void GUISwapUrl (GtkWidget *, gpointer)
**
** Wrapper for insertion of download URL.
*************************************/
{	int row1, row2=-1;
	char number[4];
	char *dest = entry_popup("Swap", "Swap with which row number?");

	row1 = GPOINTER_TO_INT((GTK_CLIST(filelist)->selection)->data);
	if (dest == NULL)
		return;
	row2 = atoi(dest)-1; 
	if (row2 < 0 || row2 > GTK_CLIST(filelist)->rows+1 || row1 == row2)
		return;
	gtk_clist_swap_rows(GTK_CLIST(filelist), row1, row2);
	sprintf(number, "%d", row1+1);
	gtk_clist_set_text(GTK_CLIST(filelist), row1, 0, number);
	sprintf(number, "%d", row2+1);
	gtk_clist_set_text(GTK_CLIST(filelist), row2, 0, number);
	free(dest);
}

static void GUIPauseDownload (GtkWidget *w, gpointer data)
/*************************************
**	void GUIPauseUrl (GtkWidget *, gpointer)
**
** Pause child.
*************************************/
{	URLp dlurl = (URLp)data;
	fprintf(out, "%d:pause\n", dlurl->pid);
	fflush(out);
	gtk_label_set_text(GTK_LABEL(dlurl->label), "Paused");
}

void GUIStopDownload (GtkWidget *w, gpointer data)
/*************************************
**	void GUIStopUrl (GtkWidget *, gpointer)
**
** Stop download (by quiting child).
*************************************/
{	char *url;
	int i;
	URLp dlurl = (URLp)data;

	/* set status... */
	for ( i = 0; i < GTK_CLIST(filelist)->rows; i++ ) 
	{	gtk_clist_get_text(GTK_CLIST(filelist), i, 1, &url);
		if (!strcmp(dlurl->name, url))
		{	gtk_clist_set_text(GTK_CLIST(filelist), i, 2, fileStatus[4]);
			break;
		}
	}

	fprintf(out, "%d:stop\n", dlurl->pid);
	fflush(out);
}

void ChildParser (int signum)
/*************************************
**	void ChildParser (int signum)
**
** Signal handler for child process. Maybe too long?
*************************************/
{	char temp[200], *p;
	fd_set set;
	struct timeval timeout;

	signal( SIGIO, ChildParser );

	fprintf(stderr, "%d id, %d pid", getpid(), getpid());
	fflush(stderr);
	FD_ZERO(&set);
	FD_SET(filedes[0], &set);

	timeout.tv_sec = 0;
	timeout.tv_usec = 10;

	/* `select' returns 0 if timeout, 1 if input available, -1 if error. */
	if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) == 1)
		fgets(temp, 199, in);
	else
		return;
	
	p = strchr(temp, ':');
	*p++ = '\0';
	if (atoi(temp) != getpid())
		return;
	
	if (strstr(p, "pause")) {
		fd_set rfds;

		FD_ZERO(&rfds);
		FD_SET(filedes[0], &rfds);

		do { 
			select(1, &rfds, NULL, NULL, NULL);
			fgets(temp, 199, in);
	
			p = strchr(temp, ':');
			*p++ = '\0';
			if (atoi(temp) != getpid())
				continue;
			else if (!strstr(p, "pause")) {
				return;
			} 
			else if (!strstr(p, "stop")) {
				_exit(0);
			}
		} while ( 0 == 0 );

	}
	else if (strstr(p, "stop"))
	{	_exit(0);
	}
}

void ParentParser (gpointer data, gint source, GdkInputCondition condition)
/*************************************
**	void ParentParser (gpointer, gint, GdkInputCondition)
**
** Parses child messages.
*************************************/
{	char temp[200], *p, *s;
	URLp dlurl = NULL;
	int id;

	fgets(temp, 199, in);
	p = strchr(temp, ':');
	*p++ = '\0';
	id = atoi(temp);

	curr = list->next;
	while (curr != NULL)
	{	if (curr->URL.pid == id)
		{	dlurl = &curr->URL;
			break;
		}
		curr = curr->next;
	}
	if (curr == NULL)
	{	message_box("Error", "ParentParser");
		return;
	}
		

	s = strchr(p, ':');
	*s++ = '\0';
	if (p[0] == 'm' && p[1] == 'b')
	{	p = strchr( s, '#');
		while ( *p != '\0') {
			if ( *p == '$' )
				*p = '\n';
			p++;
		}
		p = strchr( s, '#');
		*p = '\0';
		message_box(s, p);
		return;
	}
	else if (p[0] == 'l' && p[1] == 'b')
	{	p = s;
		while ( *p != '\0') {
			if ( *p == '$' )
				*p = '\n';
			p++;
		}
		gtk_label_set_text(dlurl->label, s);
		return;
	}
	else if (p[0] == 'p' && p[1] == 'b') 
	{	gtk_progress_bar_update(dlurl->bar, atof(s));
		return;
	}
	else 
		message_box("Bug", "Unknown message in ParentParser");
}
	
static void GUIStartUrl (GtkWidget *w, gpointer data)
/*************************************
**	void GUIStartUrl (GtkWidget *, gpointer)
**
** Wrapper for insertion of download URL.
*************************************/
{	int row;
	char *url;
	URLp dlurl = NULL;
	curr = list->next; /* don't ask me, I'm only responsible for the GUI. */

	if (curr == NULL)
		return;
	row = GPOINTER_TO_INT((GTK_CLIST(filelist)->selection)->data);
	gtk_clist_get_text(GTK_CLIST(filelist), row, 1, &url);
	while (curr != NULL)
	{	if (!strcmp(curr->URL.name, url))
		{	dlurl = &curr->URL;
			break;
		}
		curr = curr->next;
	}
	if (curr == NULL)
	{	message_box("Error", "An error occurred:\n"
		"GUIStartUrl reached curr == NULL");
		return;
	}

	dlurl->pid = fork();
	if (dlurl->pid == 0) /* child */
	{	/* The forking wastes resources, as it
		   have access to the filelist and the NODE* linked list. This
		   can be a problem (and possibly a security risk), as file
		   descriptors are copied, what may result in a bunch of unused
		   fd's wasting the system resources. This can be avoided by a
		   more proper use of the linked lists, and should be done ASAP. */
		struct sigaction new_action;
		
		dlurl->pid = getpid(); /* our id in comm */

/*		new_action.sa_handler = ChildParser;
		sigemptyset( &new_action.sa_mask );
		new_action.sa_flags = 0;
		new_action.sa_restorer = NULL;
		sigaction(SIGIO, &new_action, NULL); */
		signal( SIGIO, ChildParser );
		fcntl(filedes[0], F_SETOWN, getpid());
		fcntl(filedes[0], F_SETFL, O_ASYNC);
		
		DownloadLoop(dlurl);

		_exit(0);
	}
	else if (dlurl->pid < 0)
	{	message_box("Error", "Could not fork!\nCannot download.");
	}
	else
	{
		GtkWidget *newwin;
		GtkWidget *vbox;
		GtkWidget *pbar;
		GtkWidget *label;
		GtkWidget *button;
		GtkAdjustment *adj;

		newwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
		gtk_signal_connect(GTK_OBJECT(newwin), "destroy", 
			GTK_SIGNAL_FUNC(GUIStopDownload), dlurl);
		gtk_signal_connect(GTK_OBJECT(newwin), "destroy", 
			GTK_SIGNAL_FUNC(gtk_widget_destroy), newwin);
		gtk_window_set_title(GTK_WINDOW(newwin), "Downloading");
		gtk_container_set_border_width(GTK_CONTAINER(newwin), 0);

		vbox = gtk_vbox_new(FALSE, 0);
		gtk_container_add(GTK_CONTAINER(newwin), vbox);
		gtk_widget_show(vbox);

		/* URL */
		label = gtk_label_new(dlurl->name);
		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
		gtk_widget_show(label);

		/* progress bar */
		adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 100, 0, 0, 0);
		pbar = gtk_progress_bar_new_with_adjustment(adj);
		gtk_progress_set_format_string(GTK_PROGRESS(pbar), "%p%% complete");
		gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE );
		gtk_box_pack_start(GTK_BOX(vbox), pbar, FALSE, FALSE, 10);
		gtk_widget_show(pbar);
		dlurl->bar = GTK_PROGRESS_BAR(pbar);

		/* a nice label, with bytes downloaded */
		label = gtk_label_new("0 bytes downloaded");
		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
		gtk_widget_set_usize(label, 200, 200);
		gtk_widget_show(label);
		dlurl->label = GTK_LABEL(label);

		button = gtk_button_new_with_label("Stop");
		gtk_signal_connect(GTK_OBJECT(button), "clicked",
			GTK_SIGNAL_FUNC(GUIStopDownload), dlurl);
		gtk_container_add(GTK_CONTAINER(vbox), button);
		gtk_widget_show(button);

		button = gtk_button_new_with_label("Pause/Unpause");
		gtk_signal_connect(GTK_OBJECT(button), "clicked",
			GTK_SIGNAL_FUNC(GUIPauseDownload), dlurl);
		gtk_container_add(GTK_CONTAINER(vbox), button);
		gtk_widget_show(button);
		gtk_widget_show(newwin);
		gtk_clist_set_text(GTK_CLIST(filelist), row, 2, fileStatus[1]);
	}
}

void CreateMainWindow (void) 
/*************************************
**	void CreateMainWindow (void)
**
** Creates main window.
*************************************/
{	GtkWidget *window;
	GtkWidget *hbox, *vbox;
	GtkWidget *label;
	GtkWidget *entry;
	GtkWidget *button;
	GtkWidget *scrolled_win;
	int piper;

	
	if ((piper = pipe(filedes)) == -1) {
		printf("GUI Fatal error: Cannot open pipe.");
		exit(1);
	}
	in = fdopen(filedes[0], "r");
	out = fdopen(filedes[1], "w");
	setvbuf( out, NULL, _IONBF, 0 );

	/* main window */	
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_usize(window, 600, 300);
	gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
	gtk_signal_connect(GTK_OBJECT(window), "destroy",
		GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
	gtk_window_set_title(GTK_WINDOW(window), "Get and Resume Elite EDition (GREED)");
	gtk_container_set_border_width(GTK_CONTAINER(window), 0);

	vbox = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(window), vbox);
	gtk_widget_show(vbox);

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(vbox), hbox);
	gtk_widget_set_usize(hbox, 600, 50);
	gtk_widget_show(hbox);

	/* entry text to insert a new URL in the list */
	label = gtk_label_new("File URL:");
	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
	//gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, FALSE, 0);
	gtk_widget_show(label);

	entry = gtk_entry_new();
	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, FALSE, 0);
	gtk_entry_set_text(GTK_ENTRY(entry), "ftp://127.0.0.1/pub/tut1.ps");
	gtk_signal_connect(GTK_OBJECT(entry), "activate",
		GTK_SIGNAL_FUNC(GUIInsertUrl), entry);
	gtk_widget_set_usize(entry, 400, 0);
	gtk_widget_show(entry);

	button = gtk_button_new_with_label("Insert");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
		GTK_SIGNAL_FUNC(GUIInsertUrl), entry);
	gtk_widget_show(button);

	/* file clist */
	filelist = gtk_clist_new( 3 );
	gtk_clist_set_column_title( GTK_CLIST(filelist), 0, "Number" );
	gtk_clist_set_column_width( GTK_CLIST(filelist), 0, 50 );
	gtk_clist_set_column_title( GTK_CLIST(filelist), 1, "URL" );
	gtk_clist_set_column_title( GTK_CLIST(filelist), 2, "Status" );
	gtk_clist_set_column_width( GTK_CLIST(filelist), 1, 400 );
	gtk_clist_set_selection_mode( GTK_CLIST(filelist), GTK_SELECTION_BROWSE );
	gtk_clist_column_titles_show( GTK_CLIST(filelist) );
/*	gtk_signal_connect( GTK_OBJECT(filelist), "select_row",
	        	    GTK_SIGNAL_FUNC(),  ); */
/*	gtk_signal_connect( GTK_OBJECT(filelist), "click_column",
	        	    GTK_SIGNAL_FUNC(sort),  ); */
	
	scrolled_win = gtk_scrolled_window_new(NULL, NULL);
	gtk_container_add(GTK_CONTAINER(scrolled_win), filelist);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
	        			GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
	gtk_container_set_border_width (GTK_CONTAINER(scrolled_win), 0);
	gtk_box_pack_start(GTK_BOX(vbox), scrolled_win, TRUE, FALSE, 0);
	gtk_widget_set_usize(scrolled_win, 600, 200);
	gtk_widget_show(filelist);
	gtk_widget_show(scrolled_win);

	hbox = gtk_hbutton_box_new();
	gtk_container_add(GTK_CONTAINER(vbox), hbox);
	gtk_widget_show(hbox);

	button = gtk_button_new_with_label("Remove");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
		GTK_SIGNAL_FUNC(GUIRemoveUrl), entry);
	gtk_widget_show(button);

	button = gtk_button_new_with_label("Remove All");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
		GTK_SIGNAL_FUNC(GUIRemoveAllUrl), entry);
	gtk_widget_show(button);

	button = gtk_button_new_with_label("Swap");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
		GTK_SIGNAL_FUNC(GUISwapUrl), entry);
	gtk_widget_show(button);

	button = gtk_button_new_with_label("Download");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
		GTK_SIGNAL_FUNC(GUIStartUrl), entry);
	gtk_widget_show(button);

	button = gtk_button_new_with_label("Quit");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
		GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
	gtk_widget_show(button);

	gdk_input_add(filedes[0], GDK_INPUT_READ, ParentParser, NULL);
	gtk_widget_show(window);
}
