/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#+
#+     Glade / Gtk Programming
#+
#+     Copyright (C) 2019 by Kevin C. O'Kane
#+
#+     Kevin C. O'Kane
#+     kc.okane@gmail.com
#+     https://www.cs.uni.edu/~okane
#+     http://threadsafebooks.com/
#+
#+ 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
#+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gtk/gtkx.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <sys/mman.h>

// Make them global

GtkWidget	*window;
GtkWidget	*fixed1;
GtkWidget	*button1;
GtkWidget	*button2;
GtkWidget	*button3;
GtkWidget	*button4;
GtkWidget	*label1;
GtkWidget	*label2;
GtkWidget	*label3;
GtkWidget	*radio1;
GtkWidget	*radio2;
GtkWidget	*radio3;
GtkWidget	*check1;
GtkWidget	*toggle1;
GtkWidget	*spin1;
GtkWidget	*switch1;
GtkWidget	*combo1;
GtkWidget	*entry1;
GtkWidget	*color1;
GtkWidget	*file1;
GtkWidget	*font1;
GtkWidget	*volume1;
GtkWidget	*scroll1;
GtkWidget	*frame1;
GtkWidget	*frame2;
GtkWidget	*frame3;
GtkWidget	*frame4;
GtkWidget	*image1;
GtkWidget	*image2;
GtkWidget	*timer;
GtkWidget	*menu1;
GtkWidget	*new;
GtkWidget	*vc1;
GtkWidget	*prog1;
GtkWidget	*start1;
GtkWidget	*PingTime;
GtkWidget	*PingTimeDisplay;

GtkBuilder	*builder; 

GtkListStore	*liststore1;
GtkAdjustment	*adjustment2;

char		*pingtotal; // pointer shared memory
int		*pingFlag;
void		on_destroy(); 

int	countDownMax, countCurrent;
int	currentRow;
char	rows[5][10] = 
		{ "row 1", "row 2", "row 3", "row 4", "row 5" };

gboolean timer_handler(GtkWidget *);

int main(int argc, char *argv[]) {

	gtk_init(&argc, &argv); // init Gtk

//---------------------------------------------------------------------
// establish contact with xml code used to adjust widget settings
//---------------------------------------------------------------------
 
	builder = gtk_builder_new_from_file ("part1.glade");
 
	window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));

	g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), NULL);

        gtk_builder_connect_signals(builder, NULL);

	fixed1 = GTK_WIDGET(gtk_builder_get_object(builder, "fixed1"));
	button1 = GTK_WIDGET(gtk_builder_get_object(builder, "button1"));
	button2 = GTK_WIDGET(gtk_builder_get_object(builder, "button2"));
	button3 = GTK_WIDGET(gtk_builder_get_object(builder, "button3"));
	button4 = GTK_WIDGET(gtk_builder_get_object(builder, "button4"));
	label1 = GTK_WIDGET(gtk_builder_get_object(builder, "label1"));
	label2 = GTK_WIDGET(gtk_builder_get_object(builder, "label2"));
	label3 = GTK_WIDGET(gtk_builder_get_object(builder, "label3"));
	radio1 = GTK_WIDGET(gtk_builder_get_object(builder, "radio1"));
	radio2 = GTK_WIDGET(gtk_builder_get_object(builder, "radio2"));
	radio3 = GTK_WIDGET(gtk_builder_get_object(builder, "radio3"));
	check1 = GTK_WIDGET(gtk_builder_get_object(builder, "check1"));
	toggle1 = GTK_WIDGET(gtk_builder_get_object(builder, "toggle1"));
	switch1 = GTK_WIDGET(gtk_builder_get_object(builder, "switch1"));
	combo1 = GTK_WIDGET(gtk_builder_get_object(builder, "combo1"));
	entry1 = GTK_WIDGET(gtk_builder_get_object(builder, "entry1"));
	spin1 = GTK_WIDGET(gtk_builder_get_object(builder, "spin1"));
	color1 = GTK_WIDGET(gtk_builder_get_object(builder, "color1"));
	font1 = GTK_WIDGET(gtk_builder_get_object(builder, "font1"));
	file1 = GTK_WIDGET(gtk_builder_get_object(builder, "file1"));
	volume1 = GTK_WIDGET(gtk_builder_get_object(builder, "volume1"));
	frame1 = GTK_WIDGET(gtk_builder_get_object(builder, "frame1"));
	frame2 = GTK_WIDGET(gtk_builder_get_object(builder, "frame2"));
	frame3 = GTK_WIDGET(gtk_builder_get_object(builder, "frame3"));
	frame4 = GTK_WIDGET(gtk_builder_get_object(builder, "frame4"));
	scroll1 = GTK_WIDGET(gtk_builder_get_object(builder, "scroll1"));
	image2 = GTK_WIDGET(gtk_builder_get_object(builder, "image2"));
	timer = GTK_WIDGET(gtk_builder_get_object(builder, "timer"));
	menu1 = GTK_WIDGET(gtk_builder_get_object(builder, "menu1"));
	new = GTK_WIDGET(gtk_builder_get_object(builder, "new"));
	vc1 = GTK_WIDGET(gtk_builder_get_object(builder, "vc1"));
	prog1 = GTK_WIDGET(gtk_builder_get_object(builder, "prog1"));
	start1 = GTK_WIDGET(gtk_builder_get_object(builder, "start1"));
	PingTime = GTK_WIDGET(gtk_builder_get_object(builder, "PingTime"));
	PingTimeDisplay = GTK_WIDGET(gtk_builder_get_object(builder, "PingTimeDisplay"));

	liststore1 = GTK_LIST_STORE(gtk_builder_get_object(builder, "liststore1"));
	adjustment2 = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "adjustment2"));

//	Note include of <sys/mman.h> at beginning

        pingtotal = mmap(NULL, 512, PROT_READ | PROT_WRITE,
                MAP_SHARED | MAP_ANONYMOUS, -1, 0); // shared memory

	strcpy(pingtotal,"Tot Play Time: <empty>");

        pingFlag = mmap(NULL, 32, PROT_READ | PROT_WRITE,
                MAP_SHARED | MAP_ANONYMOUS, -1, 0); // shared memory

	*pingFlag = 1;

	/*******************
	struct GtkAdjustment {
                    gdouble lower,
                    gdouble upper,
                    gdouble step_increment,
                    gdouble page_increment,
                    gdouble page_size};
	****************************/

	/* alter the range of the scroll bar */
        gtk_range_set_range (GTK_RANGE(scroll1), 0, 100);

        GdkColor color; // default background color
        color.red = 0xcccc;
        color.green = 0xcccc;
        color.blue = 0xd900;
        gtk_widget_modify_bg(GTK_WIDGET(window), GTK_STATE_NORMAL, &color);

        color.red = 0xaaaa;
        color.green = 0xaaaa;
        color.blue = 0xeeee;
        gtk_widget_modify_bg(frame1, GTK_STATE_NORMAL, &color);

        color.red = 0xaaaa;
        color.green = 0xeeee;
        color.blue = 0xaaaa;
        gtk_widget_modify_bg(frame2, GTK_STATE_NORMAL, &color);

        color.red = 0xeeee;
        color.green = 0xaaaa;
        color.blue = 0xaaaa;
        gtk_widget_modify_bg(frame3, GTK_STATE_NORMAL, &color);

        color.red = 0xeeee;
        color.green = 0xeeee;
        color.blue = 0xaaaa;

        gtk_widget_modify_bg(frame4, GTK_STATE_NORMAL, &color);

	image1 = NULL; 

//	The following function returns an ID of the event that can be
//	used to destroy it. Not used here.

	guint ID = g_timeout_add_seconds(1, (GSourceFunc) timer_handler, timer);

//	Time units smaller than seconds are possible (higher overhead):
//	guint ID = g_timeout_add(250, (GSourceFunc) timer_handler, data_pointer);

	countCurrent = countDownMax = 0;

//	label3 & button4 scroll and background setup

	currentRow = 0;
	gtk_label_set_text (GTK_LABEL(label3), (const gchar* ) rows[currentRow]);

        color.red = 0xdddd;
        color.green = 0xcccc;
        color.blue = 0xd900;
        gtk_widget_modify_bg(label3, GTK_STATE_NORMAL, &color); // label background

	gtk_widget_set_events(button4, GDK_SCROLL_MASK); // allowed events.
                					// see GdkEventScroll data
							// structure for valid elements

	gtk_widget_show(window);

	gtk_main();

	return EXIT_SUCCESS;
	}

//......................................................................................

void	on_button1_clicked (GtkButton *b) {
	char tmp[128];
	gdouble val = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin1));
	sprintf(tmp, "spin=%d", (int) val);
	gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) tmp);
	}

void	on_radio1_toggled(GtkRadioButton *b) {
	gboolean T = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b));
	if (T) gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Radio 1 Active");
	else gtk_label_set_text (GTK_LABEL(label2), (const gchar* ) "Radio 1 Not Active");
	}

void	on_radio2_toggled(GtkRadioButton *b) {
	gboolean T = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b));
	if (T) gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Radio 2 Active");
	else gtk_label_set_text (GTK_LABEL(label2), (const gchar* ) "Radio 2 Not Active");
	}

void	on_radio3_toggled(GtkRadioButton *b) {
	gboolean T = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b));
	if (T) gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Radio 3 Active");
	else gtk_label_set_text (GTK_LABEL(label2), (const gchar* ) "Radio 3 Not Active");
	}

void	on_check1_toggled(GtkCheckButton *b) {
	gboolean T = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b));
	if (T) gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Check 1 Active");
	else gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Check 1 Not Active");
	}

void	on_toggle1_toggled(GtkToggleButton *b) {
	gboolean T = gtk_toggle_button_get_active(b);
	if (T) gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Toggle 1 Active");
	else gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Toggle 1 Not Active");
	}

void	on_switch1_state_set (GtkSwitch *s) {
	gboolean T = gtk_switch_get_active(s);
	if (T) gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Switch 1 Active");
	else gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) "Switch 1 Not Active");
	}

void	on_combo1_changed(GtkComboBox *c) {
	printf("combo1 changed entered\n");
	}

void	on_color1_color_set (GtkColorButton *c) {
	GdkRGBA color;
	gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER(c), &color);
	printf("red %f\n", color.red);
	printf("green %f\n", color.green);
	printf("blue %f\n", color.blue);
	printf("alpha %f\n\n", color.alpha);
	/* just for fun, lets change the scrollbar background */
	gtk_widget_override_background_color (scroll1, GTK_STATE_FLAG_NORMAL, &color);
	}

void	on_file1_file_set(GtkFileChooserButton *f) {
	printf("file name = %s\n", gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(f)) );
	printf("folder name = %s\n", gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER(f)) );
	}

void	on_font1_font_set(GtkFontButton *f1) {
	printf("font name = %s\n", gtk_font_button_get_font_name (f1) );
	}

void	on_volume1_value_changed (GtkVolumeButton *v1) {

	/*******************************************************************
	The function:
		void gtk_scale_button_set_value (GtkScaleButton *, gdouble)
	can be used to set the volume control to a particular value.
	********************************************************************/

	printf("volume scale = %f\n", gtk_scale_button_get_value(GTK_SCALE_BUTTON(v1)) );
	}

void    on_scroll1_value_changed(GtkRange *r) {
        gdouble x = gtk_range_get_value (r);
	printf("scroll = %d\n", (int) x );
        }
	
void	on_entry1_changed(GtkEntry *e) {
	char tmp[128];
	sprintf(tmp, "entry=%s", gtk_entry_get_text(e));
	gtk_label_set_text (GTK_LABEL(label1), (const gchar* ) tmp);
	}

void	on_button2_clicked(GtkButton *b) {
	gtk_editable_delete_text (GTK_EDITABLE(entry1), 0, -1);
	}

/*-------------------------------------------------
void	on_button3_clicked(GtkButton *b) {
	char filename[] = "prr.jpg";
	int hor = 150, ver = 1;
	if (image1) 
		gtk_container_remove (GTK_CONTAINER (fixed1), image1); // remove old slide
	gtk_widget_hide(image2);
        image1 = gtk_image_new_from_file (filename);
        gtk_container_add (GTK_CONTAINER (fixed1), image1);
        gtk_widget_show(image1);
        gtk_fixed_move (GTK_FIXED(fixed1), image1, hor, ver);
	}
-------------------------------------------------*/

void	on_button3_clicked(GtkButton *b) {
	char filename[] = "nya.jpg";
	char cmd[2048];
	FILE *f1;
	int j, h, v, hor = 150, ver = 1;

	if (image1) 
		gtk_container_remove (GTK_CONTAINER (fixed1), image1); // remove old slide
	gtk_widget_hide(image2);

//	---------------------------------------------------------------
//	get dimensions of image. identify() is part of ImageMagick
//	---------------------------------------------------------------

	sprintf(cmd, "identify -format %%wx%%h \"%s\"\n", filename);

	f1 = popen(cmd, "r");

	strcpy(cmd,"");
	fgets(cmd, 512, f1);
	fclose (f1);

	h = v = 1;

	if (strlen(cmd)) {
		for (j=0; j<strlen(cmd)-1; j++) if (cmd[j] == 'x') break;
		if (cmd[j] == 'x') { // x is a delimiter between horizontal and vertical
			cmd[j] = 0;
			sscanf(cmd,"%d",&h);
			sscanf(&cmd[j+1], "%d", &v);
			}
		}

	if (h < 100 || v < 100 ) {
		printf("**** questionable image: %s\n",filename);
		return;	// probably a bad image
		}

//	---------------------------------------------------------------
//	RESIZE image.  convert() is part of ImageMagick
//	---------------------------------------------------------------

	int width = 420; 
	int height = 250;

	sprintf(cmd, "convert \"%s\" -resize %dx%d tmp.jpg", filename, width, height);
	system(cmd);

	strcpy(filename,"tmp.jpg");

//	---------------------------------------------------------------
//	Get new image dimensions  - actual dimensions will be in the
//	range of the conversion. Conversion will maintain ratio.
//	---------------------------------------------------------------

	sprintf(cmd, "identify -format %%wx%%h \"%s\"\n", filename);
	f1 = popen(cmd, "r");
	strcpy(cmd,"");
	fgets(cmd, 512, f1);
	fclose (f1);
	h = v = 1;

	if (strlen(cmd)) {
		for (j=0; j<strlen(cmd)-1; j++) if (cmd[j] == 'x') break;
		if (cmd[j] == 'x') {
			cmd[j] = 0;
			sscanf(cmd,"%d",&h);
			sscanf(&cmd[j+1], "%d", &v);
			}
		}

//	---------------------------------------------------------------
//	h and v are now the actual new dimensions which you can use
//	to better center your image in the GUI - not used here
//	---------------------------------------------------------------

	image1 = gtk_image_new_from_file (filename);

	gtk_container_add (GTK_CONTAINER (fixed1), image1);
	gtk_widget_show(image1);

	gtk_fixed_move (GTK_FIXED(fixed1), image1, hor, ver);

	system("rm tmp.jpg");

	}

gboolean	timer_handler(GtkWidget *timer) {

		time_t t = time(0);
		gtk_label_set_text(GTK_LABEL(timer), ctime(&t));    // update time of day

		if (countCurrent) {
			countCurrent = countCurrent - 1;
			gtk_progress_bar_set_fraction 
				(GTK_PROGRESS_BAR(prog1), (gdouble) countCurrent/countDownMax );
			}

//		Extract contents of string showing total play time.
//		This string is set in a forked process and the pointer
//		pingtotal is in shared memory

		gtk_label_set_text (GTK_LABEL(PingTimeDisplay), (const gchar* ) pingtotal);

		return TRUE; // FALSE kills the timer
		}

void	on_new_activate(GtkMenuItem *m) {
	printf("New activated\n");
	}

void	on_vc1_activate(GtkMenuItem *m) {
	printf("Vc1 activated\n");
	}

void	on_new_select(GtkMenuItem *m) {
	printf("New selected\n");
	}

void	on_new_deselect(GtkMenuItem *m) {
	printf("New deselected\n");
	}

void	on_start1_clicked (GtkButton *b) {
	countDownMax = (int) gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin1));
	if (countDownMax <= 0 ) countCurrent = countDownMax = 0;
	else {
		countCurrent = countDownMax;
		gtk_progress_bar_set_fraction 
			(GTK_PROGRESS_BAR(prog1), (gdouble) 1.00 );
		}
	}

gboolean on_start1_button_press_event (GtkButton *btn, GdkEventButton *event) {

	printf("event button value: %d\n", event->button);

        if (event->button < 3 ) return FALSE; // left or center click - continue processing

	countDownMax = countCurrent = 0;
	gtk_progress_bar_set_fraction 
		(GTK_PROGRESS_BAR(prog1), (gdouble) 0.00 );
        return TRUE; // stop processing of this click
        }

gboolean on_button4_scroll_event(GtkButton *b, GdkEventScroll *e) {

                // see docs: GdkEventScroll data structure for valid elements
                // see above: gtk_widget_set_events(button4,GDK_SCROLL_MASK);

                if (e->direction == 1) { // down
			if (currentRow < 4) {
				currentRow ++;
				gtk_label_set_text 
					(GTK_LABEL(label3), (const gchar* ) rows[currentRow]);
				return TRUE;
				}
			return TRUE;
                        }
                else { // up
			if (currentRow > 0) {
				currentRow --;
				gtk_label_set_text 
					(GTK_LABEL(label3), (const gchar* ) rows[currentRow]);
				return TRUE;
				}
			return TRUE;
                        }
                return TRUE;
                }

void	on_PingTime_clicked (GtkButton *b) {

	pid_t i = fork();
	if (i) return; // parent task returns

	FILE *f1 = popen("ping -c 10 www.cs.uni.edu", "r");

	if (f1 == NULL) exit(1);

	while (1) {
		char name[512];
		char * p1;

		if (*pingFlag == 0) {
			fclose(f1);
			exit(0);
			}

		if (fgets(name, 512, f1) == NULL) {
			strcpy(pingtotal, "done");
			fclose(f1);
			exit(0);
			}
		
		p1 = strstr(name, "time=");
		if (p1 == NULL) continue; // first line has no "time="

		printf("ping result=%s\n", p1);
		strcpy(pingtotal, p1);
		}
	}

void	on_destroy() { 
		*pingFlag = 0; // note dereference operator
		gtk_main_quit();
		}
