#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <gtk/gtk.h>
#include "main.h"

typedef struct {
	double **data ;
	double max ;
	double min ;
	int *npoints ;
	int maxpoints ;
	int nseries ;
	char **names ;
	int maxname ;
	char *title ; } graph_s ;

/* this function does the actual drawing of the sequence gc graph */
void gc_draw_graph(GtkWidget *picture, GdkEventExpose *event, gpointer data) {
	GdkDrawable *gdkpic ;
	GdkGC *pen, *black, *white ;
	GtkStyle *style ;
	graph_s *graph ;
	data_s *d ;
	opt_s *o ;
	double scalex, scaley ;
	int i, j, x, y, xo, yo, 
		height, width, marginx, marginy, 
		dheight, dwidth, lheight, lwidth, lboxheight, lboxwidth,
		drawlegend = TRUE, color ;

	/* casting data into apropriate types */
	d = (data_s *) data ;
	o = (opt_s *) d->data ;
	graph = (graph_s *) d->data1 ;

	gdkpic = picture->window ;
	black = picture->style->black_gc ;
	white = picture->style->white_gc ;

	/* create a custom pen */
	pen = gdk_gc_new(picture->window) ;
	style = gtk_widget_get_style(picture) ;

	height = picture->allocation.height ;
	width = picture->allocation.width ;

	gdk_draw_rectangle(gdkpic, white, TRUE, 0, 0, width, height) ;

	/* margins */
	marginx = 0.05 * width ;
	marginy = 0.05 * height ;

	/* calculating what part for the picture, what part for the legend */
	dheight = height - 2*marginy ;

	lwidth = graph->maxname + 30 + marginx ;
	
	if( lwidth < 0.5 * width) {
		drawlegend = TRUE ;
		dwidth = width - 2*marginx - graph->maxname - 30;
		lwidth = (0.5 * width - graph->maxname) / 2 ;
		lheight = 0.8 * dheight / graph->nseries ;
		lboxheight = 0.8 * lheight ;
		if(lboxheight > 20) lboxheight = 20 ;
		lboxwidth = 0.8 * lwidth ;
		if(lboxwidth > 20) lboxwidth = 20 ;
	} else {
		dwidth = width - 2*marginx ;
		drawlegend = FALSE ;
	}


	/* drawing scale */
	if(marginx) {
			gdk_draw_line(gdkpic, black, marginx, (height - marginy), 
				(dwidth + marginx), (height - marginy) ) ;
			gdk_draw_line(gdkpic, black, 
				.5 * marginx, (height - marginy), marginx, (height - marginy) ) ;
			gdk_draw_line(gdkpic, black, 
				.5 * marginx, (height / 2), marginx, (height / 2) ) ;
			gdk_draw_line(gdkpic, black, 
				.5 * marginx, marginy, marginx, marginy ) ;
			gdk_draw_line(gdkpic, black, 
				.7 * marginx, marginy + .25 * dheight, 
					marginx, marginy  + .25 * dheight) ;
			gdk_draw_line(gdkpic, black, 
				.7 * marginx, marginy + .75 * dheight , 
					marginx, marginy  + .75 * dheight) ;
	}

	if(marginy) 
			gdk_draw_line(gdkpic, black, marginx, marginy, 
				marginx, marginy + dheight ) ;

	scalex = (double) graph->maxpoints / (double) dwidth ;
	scaley = (double) (graph->max - graph->min) / (double) dheight ;

	/* for each series in the graph structure */
	
	for(i = 0, color = 0 ; i < graph->nseries ; i++, color++ ) {

		if(drawlegend) {
			gdk_draw_rectangle(gdkpic, pen, TRUE, 
				(dwidth + marginx + 5), marginy + i * lheight, 20, lboxheight) ;
			gdk_draw_string(gdkpic, style->font, black, 
				(dwidth + marginx + 30), marginy + (i + 0.5) * lheight, graph->names[i]) ;
		}

		xo = 0 ; yo = 0 ;

		color %= o->ncolors ;
		gdk_gc_set_foreground(pen, &o->colors[color]) ;

		for(j = 0 ; j < graph->npoints[i] ; j++) {

			x = marginx + ((1.0 * j) / scalex) ;
			y = marginy + (dheight - graph->data[i][j] / scaley) ;

			if(xo == 0) { xo = x ; yo = y ; }

			gdk_draw_line(gdkpic, pen, xo, yo, x, y ) ;
			xo = x ; 
			yo = y ;
		}

	}
}


/* creates a graph of the given values */
int graph_create(opt_s *o, graph_s *graph) {

	GtkWidget *picwin, *ypole, *picture ;
	GtkStyle *style ;
	data_s d ;
	int i, wid ;

	/* main graph window */
	picwin = gtk_window_new(GTK_WINDOW_DIALOG) ;
	gtk_window_set_title(GTK_WINDOW(picwin), graph->title) ;
	gtk_signal_connect(GTK_OBJECT(picwin), "destroy", 
		GTK_SIGNAL_FUNC(zakoncz_dialog), picwin) ;
	gtk_accel_group_attach(o->shortcuts, GTK_OBJECT(picwin)) ;

	ypole = gtk_vbox_new(FALSE, 0) ;
	gtk_container_add(GTK_CONTAINER(picwin), ypole) ;
	gtk_widget_show(ypole) ;

	/* drawing area for the graph */
	picture = gtk_drawing_area_new() ;
	gtk_drawing_area_size(GTK_DRAWING_AREA(picture), 400, 150) ;
	gtk_box_pack_start(GTK_BOX(ypole), picture, TRUE, TRUE, 0) ;
	gtk_widget_show(picture) ;

	style = gtk_widget_get_style(picture) ;
	graph->maxname = 0 ;

	for(i = 0 ; i < graph->nseries ; i++ ) {
		wid = gdk_string_width(style->font, graph->names[i]) ;

		if(graph->names[i] && 
			wid > graph->maxname)
			graph->maxname = wid ;
	}

	/* prepare data for the configure and expose functions */
	d.window = picwin ;
	d.data = o ;
	d.data1 = graph ;

	/* connect expose event to graph drawing */
	gtk_signal_connect(GTK_OBJECT(picture), "expose_event",
		GTK_SIGNAL_FUNC(gc_draw_graph), &d) ;
	gtk_signal_connect(GTK_OBJECT(picture), "configure_event",
		GTK_SIGNAL_FUNC(gc_draw_graph), &d) ;

	gtk_widget_show(picwin) ;
	gtk_main() ;
	return EXIT_SUCCESS ;
}


/* draw a GC contents graph of the data */
void graph_gc(gpointer dane, guint signal, GtkWidget *kontrolka) {
	graph_s *graph ;
	FILE *fp ;
	sekw *tmp, *list, *cur;
	int nseq = 0, i, p, j, k, step, window = 50 ;
	opt_s *o ;

	tmp = NULL ; list = NULL ; cur = NULL ;
	o = (opt_s *) dane ;

	/* dump input window into temporary file */
	if(window_tofile(&o->panels[0], o)) return ;

	/* open temporary file */
	if( (fp = fopen(o->panels[0].tmpfil, "r")) == NULL) {
		komunikat("Cannot open temporary %s file for reading", o->panels[0].title) ;
		return ;
	}

	/* read sequence list */
	for(nseq = 0 ; (tmp = gp_seq_read(fp)) != NULL ; nseq++) {

		if(list == NULL) {
			list = tmp ; 
			cur = list ;
		} else {
			cur->next = tmp ;
			cur = cur->next ;
			cur->next = NULL ;
		}

	}

	o->seqlist = list ;

	if(list == NULL) {
		komunikat("No sequences found on input") ;
		return ;
	}

	/* allocating memory for the graph to hold all sequences */
	graph = malloc(sizeof(*graph)) ;
	graph->min = 0.0 ;
	graph->max = 100.0 ;
	graph->nseries = nseq ;
	graph->npoints = malloc(sizeof(*graph->npoints) * nseq) ;
	graph->names = malloc(sizeof(*graph->names) * nseq) ;
	graph->data = malloc(sizeof(*graph->data) * nseq) ;
	graph->title = strdup("GC contents graph") ;
	graph->maxpoints = 0 ;


	for(tmp = o->seqlist,  step = 1; tmp != NULL ; tmp = tmp->next) {
		if(tmp->leng > step) step = tmp->leng ;
	}

	if(step > 2000) {
		step = step / 2000 ;
		if(window < step) window = step ;
	} else step = 1 ;

	for(tmp = o->seqlist,  i = 0; tmp != NULL ; tmp = tmp->next, i++) {

		/* reserving space for the current sequence */
		graph->names[i] = strdup(tmp->name) ;
		graph->npoints[i] = (tmp->leng - window) / step ;
		graph->data[i] = malloc(sizeof(*graph->data[i]) * (2 + graph->npoints[i])) ;

		if(graph->npoints[i] > graph->maxpoints) 
			graph->maxpoints = graph->npoints[i] ;

		for(j = 0, p = 0; p < (tmp->leng - window); j++, p += step) {

			graph->data[i][j] = 0.0 ;

			for(k = p; k < p + window ; k++) 
				if(strchr("gcGC", tmp->sequ[k])) graph->data[i][j] += 1 ;

			graph->data[i][j] = graph->data[i][j] * 100.0 / window ;

		}

	}

	graph_create(o, graph) ;

}



