/*
 * $xforce: geomgui/geomgui.c,v 1.22 2006/05/21 08:40:49 xride Exp $
 *
 *
 * BEERWARE LICENSE
 * If you like this code, then you may buy me a beer.
 * You may use this code in any way, aslong as this text follows it.
 * Ver 42 of BEERWARE LICENSE Søren (Xride) Straarup <xride@x12.dk>
 */

/*
 * This program leaves behind two files in /tmp 
 * conf.dot, conf.xml and conf.svg
 */

#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gvc.h>
#include <unistd.h>
#include <sys/stat.h>

#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <librsvg/rsvg.h>
#include "geomgui.h"

#define BUFSIZE 100000

/*
 * Help text
 */

void usage()
{
	printf("\n\tProgram %s version %s\n",program_name,VERSION);
	printf("\tparameters:\n");
	printf("\t\t-r user@host\tHost to ssh into to grab geom layout\n");
	printf("\n");
	printf("\tkeys:\n");
	printf("\t\th\tscroll left\n");
	printf("\t\tj\tscroll up\n");
	printf("\t\tk\tscroll down\n");
	printf("\t\tl\tscroll right\n");
	printf("\t\tz\tzoom out\n");
	printf("\t\tZ\tzoom in\n");
	printf("\t\tu\tupdate\n");
	printf("\t\tq\tquit\n");
	printf("\n");
	printf("\n");
}

/*
 * this is used to "lock" geom2svg() from beeing run multiple times at once
 */

int lock_graph;

/*
 * this gets xml and makes a svg of it.
 * currently it doesn't work the second time it is called.
 */

int geom2svg(struct myconfigs *configs)
{

	char str[BUFSIZE];
	char dot[BUFSIZE];
	char buf[BUFSIZE];
	int ret;
	size_t len;

/*
 * fool graphviz to output in svg format and to a file called conf.svg
 */
	int argg=5;
	char *argh[5];
	argh[0] = malloc(3 * sizeof(char));
	argh[1] = malloc(6 * sizeof(char));
	argh[2] = malloc(3 + strlen(configs->svgfile));
	argh[3] = malloc(19 * sizeof(char));
	argh[4] = malloc(19 * sizeof(char));

	FILE *fp;
	FILE *fpxml;
	struct stat *statxml = malloc(sizeof(struct stat));
	char *bufcmd = malloc(256 * sizeof(char));

	if(!argh[0])
		perror("argh[0]");

	if(!argh[1])
		perror("argh[1]");

	if(!argh[2])
		perror("argh[2]");

	if(!argh[3])
		perror("argh[3]");

	if(!argh[4])
		perror("argh[4]");

	if(!statxml)
		perror("statxml");

	if(!bufcmd)
		perror("bufcmd");

	strcpy(argh[0],"gg");
	strcpy(argh[1],"-Tsvg");
	strcpy(argh[2],"-o");
	strcat(argh[2],configs->svgfile);
	strcpy(argh[3],"-Nfontname=luximr");
	strcpy(argh[4],"-s100");

#if DEBUG
printf("XXX -> argh[2]=%s\n",argh[2]);
#endif

/*
 * Fetch the data in xml form out of the kernel
 */
	if(strlen(configs->userhost)) {
#if DEBUG
		printf("XXX: sizeof(argh[1]) = %i\n",sizeof(argh[1]));
#endif
		snprintf(bufcmd,256 * sizeof(bufcmd)
				,"ssh %s 'sysctl -b kern.geom.confxml' > %s"
				,configs->userhost,configs->xmlfile);
#if DEBUG
		printf("XXX: bufcmd %s\n",bufcmd);
#endif
		ret = system(bufcmd);
		if (ret) 
			perror("system");
	} else {
		len = sizeof(str);
		ret = sysctlbyname("kern.geom.confxml",str,&len,NULL,0);
	
		fpxml = fopen(configs->xmlfile,"w+");
		if(fpxml == NULL){
			printf("error in writting to %s\n",configs->xmlfile);
			return 1;
		}

		fprintf(fpxml,"%s",str);
		fclose(fpxml);
	}

	if(ret != 0){
		printf("error in sysctl\n");
		return 1;
	}
	fp = fopen(configs->dotfile,"w+");
	if(fp == NULL){
		printf("error in writting to %s\n",configs->dotfile);
		return 1;
	}

	strcpy(buf,"");
	strcpy(str,"");
	fpxml = fopen(configs->xmlfile,"r");
	ret = stat(configs->xmlfile,statxml);
	if (ret) 
		perror(configs->xmlfile);
	while(!feof(fpxml)) {
		fgets(buf,BUFSIZE,fpxml);
		if(!strlen(str))
			snprintf(str,statxml->st_size,"%s",buf);
		else
			snprintf(str,statxml->st_size,"%s%s",str,buf);
	}
	fclose(fpxml);

	//printf("XML: %s",str);

	strcpy(dot,"");
	if(geom_xml2dot(configs, dot, str) != 0 ) {
		printf("error in geom_xml2dot\n");
	}
	fprintf(fp,"digraph geom {\n\n");
	fprintf(fp,"%s",dot);
	fprintf(fp,"};\n");
/*
 * rewind the file pointer
 */
	rewind(fp);
/*
 * Do the actual graphing
 */
	Agraph_t* g;
	static GVC_t *gvc;
	if (!gvc) gvc = gvContext();

	gvParseArgs(gvc,argg,argh);
	g = agread (fp);
	gvLayout(gvc, g, &"dot");
	gvRenderJobs(gvc, g);

	fclose(fp);
	gvFreeLayout(gvc, g);

/*
	free(argh[0]);
	free(argh[1]);
	free(argh[2]);
	free(argh[3]);
	free(argh[4]);
*/
	free(statxml);
	free(bufcmd);
	return 0;
}

/*
 * doh quit
 */

void quit ()
{ 
  gtk_exit (0);
}


/*
 * Scale the picture and show it for the first or twentysencond time
 */


gint CheckVal(GtkScrolledWindow *win) {
    GtkAdjustment *adj;
    adj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow *)win);
    printf("scrolledwin - lower %.2f  ", adj->lower);
    printf("upper %.2f  ", adj->upper);
    printf("value %.2f\n", adj->value);
	free(adj);
	return 0;
}

int scaleandshow(GtkWidget *window, struct myconfigs *configs, char *filename)
{
	GdkPixbuf *pixbuf_scaled;
	GdkPixbuf *pixbuf;
	GError **error;
	gint x;
	gint y;
	if (configs->scale < 1 )
		configs->scale = 1;
	if ( configs->scale > 5 )
		configs->scale = 5;

#if DEBUG
printf("XXX scale = %i\n",configs->scale);
#endif
   	pixbuf = rsvg_pixbuf_from_file(filename, error);
	if(pixbuf == NULL)
		printf("error openning %s\n\n",filename);
	x=gdk_pixbuf_get_width(pixbuf)/configs->scale;
	y=gdk_pixbuf_get_height(pixbuf)/configs->scale;

#if DEBUG
printf("XXX -> x:%i y:%i\n",x,y);
#endif

	pixbuf_scaled = gdk_pixbuf_scale_simple(pixbuf,x,y,GDK_INTERP_BILINEAR);
	if(pixbuf_scaled == NULL) {
		printf("error pixbuf_scaled\n\n");
		exit(1);
	}
	if(configs->image == NULL) {
		printf("error configs->image\n\n");
		exit(2);
	}

	gtk_image_set_from_pixbuf(GTK_IMAGE(configs->image),pixbuf_scaled);
	g_object_unref(pixbuf);
	g_object_unref(pixbuf_scaled);
	if(configs->scrolled == NULL)
		printf("error configs->scrolled\n\n");
	if(configs->image == NULL) {
		printf("error configs->image\n\n");
		exit(3);
	}


	if ( configs->first == 0)
	{
		gtk_scrolled_window_add_with_viewport((GtkScrolledWindow
							*)configs->scrolled,GTK_WIDGET(configs->image));
		configs->first = 1;
	}
	gtk_widget_queue_draw(configs->scrolled);
	gtk_widget_queue_draw(window);
	configs->hadjustment = gtk_scrolled_window_get_hadjustment(
						(GtkScrolledWindow*)configs->scrolled);
	configs->vadjustment = gtk_scrolled_window_get_vadjustment(
						(GtkScrolledWindow*)configs->scrolled);
#if DEBUG
	printf("XXX: hadjustment->lower: %.2f, hadjustment->upper: %.2f,hadjustment->value: %.2f\n", configs->hadjustment->lower,configs->hadjustment->upper,configs->hadjustment->value);
	printf("XXX: vadjustment->lower: %.2f, vadjustment->upper: %.2f,vadjustment->value: %.2f\n", configs->vadjustment->lower,configs->vadjustment->upper,configs->vadjustment->value);
#endif

	return 0;
}

/*
 * Event for key presses
 * Currently zZu
 */

gboolean keypressed(GtkWidget *window, GdkEventKey *event, gpointer data)
{
	struct myconfigs *configs;
	configs = (struct myconfigs *) data;
	gdouble temp_adj;

#if DEBUG
	printf("event->string = %s\n",event->string);
#endif

	if ( !strcmp(event->string,"z")) {
		configs->scale++;
		scaleandshow(window,configs,configs->svgfile);
	}
	if ( !strcmp(event->string,"Z")) {
		configs->scale--;
		scaleandshow(window,configs,configs->svgfile);
	}
	if ( !strcmp(event->string,"u")) {
		if ( lock_graph == 0 ) {
			lock_graph = 1;
			geom2svg(configs);
			lock_graph = 0;
			scaleandshow(window,configs,configs->svgfile);
		} else
			printf("lock_graph is not zero, graphviz is already running\n");
	}
	if ( !strcmp(event->string,"q")) {
		quit();
	}

	if ( !strcmp(event->string,"c")) {
		if ( configs->color == 0 ) {
			configs->color = 1;
		}
		else {
			configs->color = 0;
		}
		if ( lock_graph == 0 ) {
			lock_graph = 1;
			geom2svg(configs);
			lock_graph = 0;
			scaleandshow(window,configs,configs->svgfile);
		} else
			printf("lock_graph is not zero, graphviz is already running\n");
	}

	if ( !strcmp(event->string,"H")) {
		if ( configs->human == 0 ) {
			configs->human = 1;
		}
		else {
			configs->human = 0;
		}
		if ( lock_graph == 0 ) {
			lock_graph = 1;
			geom2svg(configs);
			lock_graph = 0;
			scaleandshow(window,configs,configs->svgfile);
		} else
			printf("lock_graph is not zero, graphviz is already running\n");
	}

	if ( !strcmp(event->string,"h") || event->hardware_keycode == 100) {
	configs->hadjustment = gtk_scrolled_window_get_hadjustment(
						(GtkScrolledWindow*)configs->scrolled);
	configs->hadjustment->value = configs->hadjustment->value
						- configs->hadjustment->step_increment; 
	if ( configs->hadjustment->value < configs->hadjustment->lower)
		configs->hadjustment->value = configs->hadjustment->lower;
	if ( configs->hadjustment->value > configs->hadjustment->upper)
		configs->hadjustment->value = configs->hadjustment->upper;
	//configs->hadjustment->value = temp_adj;
	gtk_scrolled_window_set_hadjustment((GtkScrolledWindow*)configs->scrolled,
						configs->hadjustment);
	gtk_adjustment_value_changed(configs->hadjustment);
#if DEBUG
	printf("XXXh: hadjustment->lower: %.2f, hadjustment->upper: %.2f,hadjustment->value: %.2f\n", configs->hadjustment->lower,configs->hadjustment->upper,configs->hadjustment->value);
#endif
	}
	if ( !strcmp(event->string,"j") || event->hardware_keycode == 98) {
	configs->vadjustment = gtk_scrolled_window_get_vadjustment(
						(GtkScrolledWindow*)configs->scrolled);
	temp_adj = configs->vadjustment->value
						- configs->vadjustment->step_increment; 
	if ( temp_adj >= configs->vadjustment->lower &&
			temp_adj <= configs->vadjustment->upper) {
	configs->vadjustment->value = temp_adj;
	gtk_scrolled_window_set_vadjustment((GtkScrolledWindow*)configs->scrolled,
						configs->vadjustment);
	gtk_adjustment_value_changed(configs->vadjustment);
#if DEBUG
	printf("XXXj: vadjustment->lower: %.2f, vadjustment->upper: %.2f,vadjustment->value: %.2f\n", configs->vadjustment->lower,configs->vadjustment->upper,configs->vadjustment->value);
#endif
	}
	}
	if ( !strcmp(event->string,"k") || event->hardware_keycode == 104) {
	configs->vadjustment = gtk_scrolled_window_get_vadjustment(
						(GtkScrolledWindow*)configs->scrolled);
	configs->vadjustment->value += configs->vadjustment->step_increment; 
	if ( configs->vadjustment->value < configs->vadjustment->lower) {
	configs->vadjustment->value = configs->vadjustment->lower;
	}
	if ( (configs->vadjustment->value+configs->vadjustment->page_size) > configs->vadjustment->upper) {
	configs->vadjustment->value = configs->vadjustment->upper - configs->vadjustment->page_size;
	}

	gtk_scrolled_window_set_vadjustment((GtkScrolledWindow*)configs->scrolled,
						configs->vadjustment);
	gtk_adjustment_value_changed(configs->vadjustment);
#if DEBUG
	printf("XXXk: vadjustment->lower: %.2f, vadjustment->upper: %.2f,vadjustment->value: %.2f\n", configs->vadjustment->lower,configs->vadjustment->upper,configs->vadjustment->value);
#endif
	}
	if ( !strcmp(event->string,"l") || event->hardware_keycode == 102) {
	configs->hadjustment = gtk_scrolled_window_get_hadjustment(
						(GtkScrolledWindow*)configs->scrolled);
	configs->hadjustment->value = configs->hadjustment->value
						+ configs->hadjustment->step_increment; 
	if ( configs->hadjustment->value < configs->hadjustment->lower)
		configs->hadjustment->value = configs->hadjustment->lower;
	if ( (configs->hadjustment->value+configs->hadjustment->page_size) > configs->hadjustment->upper)
		configs->hadjustment->value = configs->hadjustment->upper - configs->hadjustment->page_size;
	gtk_scrolled_window_set_hadjustment((GtkScrolledWindow*)configs->scrolled,
						configs->hadjustment);
	gtk_adjustment_value_changed(configs->hadjustment);
#if DEBUG
	printf("XXXl: hadjustment->lower: %.2f, hadjustment->upper: %.2f,hadjustment->value: %.2f\n", configs->hadjustment->lower,configs->hadjustment->upper,configs->hadjustment->value);
#endif
	}
	return TRUE;
}

int main(int argc, char *argv[])
{
	GtkWidget *window;
	//GtkWidget *scrolled;
	struct myconfigs *configs;
	GdkFont *font = NULL;
	char xmlfile[] = "/tmp/geomgui.xml-XXXX";
	char dotfile[] = "/tmp/geomgui.dot-XXXX";
	char svgfile[] = "/tmp/geomgui.svg-XXXX";

	mkstemp(xmlfile);
	mkstemp(dotfile);
	mkstemp(svgfile);

	printf("files used in this go around:\n");
	printf("xml file: %s\n",xmlfile);
	printf("dot file: %s\n",dotfile);
	printf("svg file: %s\n\n",svgfile);

	configs=malloc(sizeof(struct myconfigs));

	if(!configs)
		perror("configs");

	configs->xmlfile = xmlfile;
	configs->dotfile = dotfile;
	configs->svgfile = svgfile;
	configs->human = 0;
	configs->color = 0;

	int ch;
	configs->userhost = "";
	while ((ch = getopt(argc, argv, "vhcr:")) != -1) {
		switch(ch) {
			case 'a':
				break;
			case 'c':
				configs->color = 1;
				break;
			case 'h':
				configs->human = 1;
				break;
			case 'v':
				usage();
				exit(0);
				break;
			case 'r':
				configs->userhost = optarg;
				break;
			default:
				usage();
				exit(0);
		}
	}

/*
#if DEBUG
	if ( configs->userhost )
		printf("userhost: %s",configs->userhost);
#endif
*/

	lock_graph=1;
	geom2svg(configs);
	lock_graph=0;

	configs->scale = 2;
	configs->first = 0;
/*
 * Show what we got
 */
	gtk_init(&argc, &argv);
	font = gdk_fontset_load("-*-courier-*-*-*-*-*-*-*-*-*-*-*-*");
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_name(window, "geomgui");
	gtk_window_set_default_size(GTK_WINDOW(window),800,600);

	configs->scrolled = gtk_scrolled_window_new(NULL, NULL);
	configs->image = GTK_IMAGE(gtk_image_new());

	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (configs->scrolled),
		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	gtk_container_add(GTK_CONTAINER(window), configs->scrolled);

	g_signal_connect(G_OBJECT (window), "destroy",
						G_CALLBACK (quit), NULL);

	scaleandshow(window,configs,configs->svgfile);

	g_signal_connect (G_OBJECT (window),"key-press-event",
						G_CALLBACK (keypressed), configs);

	gtk_widget_show_all(window);

	gtk_main ();

/*
 * This has to be at the bottom cause it terminates the program
 */
	return 0;
}
/*
 * The end no more
 */


syntax highlighted by Code2HTML, v. 0.9.1