/* ----------------------------------------------------------------------
 * generate automatic gene maps
 * Copyright (C) 2000 January Weiner III
 * 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 <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <gd.h>
#include <gdfonts.h>
#include <gdfontmb.h>
#include <gdfontl.h>
#include <gdfontt.h>
#include <gdfontg.h>

#include "genpak.h"
#include "gp_getopt.h"

#define VERSION "0.03"
#define PROGNAME "gp_map"
#define MAXLAYER 10
#define MAXSTRING 100

char *progname ;

typedef struct {
	int deflt ;
	int white ;
	int black ;
	int blue ;
	int green ;
	int red ;
	int yellow ; } color_s ;

typedef enum { NORMAL= 0, BOX, REGION, PROMOTER } type_s ;

typedef struct {

	/* file options */
	FILE *color ;
	FILE *config ;
	FILE *in ;
	FILE *out ; 
	char outfile[FILENAME_MAX] ;

	/* yes / no options */
	int bitmap ;
	int shownames ;
	int draw_scale ;
	int mapfile ;

	/* information about total number of genes etc. */
	int no_of_genes ;
	int maxoverlay ;
	int maxlayers ;

	double scale ; /* image scale in base pairs / pixel */

	/* following is in base pairs */
	long min ;
	long max ;
	long block_length ; 
	long large_tick ;
	long small_tick ;

	/* image options: following is in pixels */
	int sizex ;
	int sizey ;
	long tmargin ;
	long lmargin ;

	int tline ;
	int nline ;

	int block_height ;
	int layer_height ;
	int scale_height ;
	int gene_height ;
	int large_tick_height ;
	int small_tick_height ;
	
	/* color options */
	char color_names[255][21];
	int color_values[255] ;
	int color_arrays[255][3] ;
	int color_number ;

	/* GD specific options */

	color_s colors ;
	gdFontPtr font ;
	gdImagePtr image ;
	
	} opt_s ;

struct gene_ss {
	long s ;
	long e ;
	int dir ;
	int color ;
	int layer ;
	int slot ;
	char url[MAXSTRING] ;
	type_s type ;
	char name[MAXSTRING] ;
	struct gene_ss * prev ; 
	struct gene_ss * next ; } ;

typedef struct gene_ss gene_s ;
	

gene_s * read_genes (opt_s o) ;
int print_gene_list (gene_s *gl,opt_s o) ;
int gene_limits (gene_s *glist, long *max, long *min) ;
int preset_options (opt_s * o) ;
int allocate_colors (opt_s * o) ;
int find_overlaps(gene_s *start, opt_s o) ;
int find_max_layer(gene_s *start) ;
int count_genes (gene_s *gg) ;
int compute_options (opt_s * o) ;
int draw_picture (gene_s *glist, opt_s o) ;
int gene_swap (gene_s *a, gene_s *b) ;

/*
 *
 */

int main(int argc, char *argv[])
{
	extern int optind ;
	extern char *optarg ;
	
	opt_s options ;
	char c ;
	long a ;

	gene_s * glist = NULL ;
	progname = argv[0] ;
	preset_options(&options) ;


	while ((c = gp_getopt(argc, argv, "Sif:m:M:b:r:Hqdvh")) != EOF)
		switch(c) {
		case 'S':
			options.shownames = FALSE ;
			if(debug) gp_warn("Not printing names") ;
			break ;
		case 'f':
			switch(optarg[0]) {
				case 'g':
					gp_warn("giant font") ;
					options.font = gdFontGiant ;
					break ;
				case 'l':
					gp_warn("large font") ;
					options.font = gdFontLarge ;
					break ;
				case 'm':
					gp_warn("medium font") ;
					options.font = gdFontMediumBold ;
					break ;
				case 's':
					gp_warn("small font") ;
					options.font = gdFontSmall ;
					break ;
				case 't':
					gp_warn("tiny font") ;
					options.font = gdFontTiny ;
					break ;
				default:
					gp_warn("wrong parameter for option -f") ;
					break ;
			}
			break ;
		case 'i':
			options.mapfile = TRUE ;
			if(debug) gp_warn("writing an html usemap file fragment to standard output") ;
			break ;
		case 'r':
			if(sscanf(optarg," %ix%i ",&options.sizex,&options.sizey) < 2) 
				gp_error("cannot read values for option -r") ;
			else 
				gp_warn("Image %i x %i pixels",options.sizex,options.sizey) ;
			break ;
		case 'b':
			if(sscanf(optarg,"%li",&options.block_length) != 1)
				gp_error("cannot read values for option -b") ;
			else
				gp_warn("block length %i",options.block_length) ;
			break ;
		case 'm':
			if(sscanf(optarg,"%li",&options.min) != 1)
				gp_error("cannot read values for option -m") ;
			else
				gp_warn("min length %i",options.min) ;
			break ;
		case 'M':
			if(sscanf(optarg,"%li",&options.max) != 1)
				gp_error("cannot read values for option -M") ;
			else
				gp_warn("max length %i",options.max) ;
			break ;
		case 'H':
			html = TRUE ;
			break ;
		case 'q':
			quiet = TRUE ;
			break ;
		case 'v':
			fprintf(stderr,"%s version %s\n",progname,VERSION) ;
			exit(0) ;
			break ;
		case 'd':
			debug = TRUE ;
			gp_warn("Running in debug mode") ;
			break ;
		case 'h':
			Help() ;
			break ;
		default:
			gp_error("Type '%s -h' for help",progname) ;
			break;
		}


/* open the file pointer to read the sequences 
 * from: standard input or a file provided? */
	if(optind >= argc) options.in = stdin ;
	else options.in = gp_file_open(argv[optind],"r") ;

/* opening the file pointer to write the output: 
 * standard output or file provided? */
	optind++ ;

	if(optind >= argc) {
		if(options.mapfile) gp_error("I need an image file name if option -i is used") ;
		else options.out = stdout ;
	} else {
		if(strlen(argv[optind]) >= FILENAME_MAX) 
			gp_error("File name too long") ;
		strcpy(options.outfile, argv[optind]) ;
		if(debug) gp_warn("Writing to file %s",options.outfile) ;
		options.out = gp_file_open(argv[optind],"wb") ;
	}

	options.color = gp_file_open("color","r") ;
	read_color_file(&options) ;

	gp_warn("Creating image...") ;
	initialize_image(&options) ;
	gp_warn("...done. Reading gene list...") ;

	glist = read_genes(options) ;
	options.maxoverlay = find_overlaps(glist,options) ;
	options.maxlayers = find_max_layer(glist) ;
	options.no_of_genes = count_genes(glist) ;

	gp_warn("...done. %i genes found.",options.no_of_genes) ;

	if(options.min == 0) gene_limits(glist,&options.min, &a) ;
	if(options.max == 0) gene_limits(glist,&a, &options.max) ;

	compute_options(&options) ;

	if(debug) {
		gp_warn("Image size %i x %i, scale %f", options.sizex,options.sizey,options.scale) ;
		gp_warn("Blocks %i bp long, %i pixels high", options.block_length, options.block_height) ;
		gp_warn("Picture from %i to %i bp", options.min, options.max) ;
	}

	draw_picture(glist, options) ;

	gp_warn("writing file...") ;
	finish_image(&options) ;
	gp_warn("...done.") ;

	if(html) gp_warn_print_all(options.out) ;
	fclose(options.color) ;
	fclose(options.out) ;
	fclose(options.in) ;
	return EXIT_SUCCESS ;
}


/* initialize and finish the image / mapfile if present */

int initialize_image(opt_s *options) {
	options->image = gdImageCreate(options->sizex, options->sizey) ;
	allocate_colors(options) ;
	if(options->mapfile) {
		fprintf(stdout,
			"<IMG SRC= \"%s\" BORDER= 0 WIDTH= %i HEIGHT= %i ALT= \"%s\" USEMAP= \"#gp_map\">\n",
			options->outfile, options->sizex, options->sizey, options->outfile) ;
		fprintf(stdout,"<MAP NAME= \"gp_map\">\n") ;
	}


	return EXIT_SUCCESS ;
}

int finish_image(opt_s *options) {
	if(options->mapfile) {
		fprintf(stdout,"<AREA SHAPE= \"DEFAULT\" NOHREF>\n") ;
		fprintf(stdout,"</MAP>\n") ;
	}
	gdImagePng(options->image,options->out) ;
	gdImageDestroy(options->image) ;
	return EXIT_SUCCESS ;
}


/* drawing genes: lowlevel computations / breaking into blocks */

int draw_gene_lowlevel (gene_s * g, opt_s o, gdPointPtr pp, int np) {
	int sblock,cblock,i,x,y1,y2 ; /* start block, current block */
	long offset = 0, block_length_scaled ;
	gdPoint ptmp[6],ptmp2[6] ;
	
	block_length_scaled = o.block_length / o.scale ;
	cblock = sblock = (g->s - o.min) / o.block_length ;

	/* add vertical offset for: */
	/* top margin and the block in which the gene is drawn */
	offset += o.tmargin + sblock * o.block_height ;

	if(g->type != REGION) {
		/* shift due to overlapping genes ("slot") */
		offset += o.gene_height / 4 * g->slot ;
		/* layer as defined in the map file */
		offset += o.layer_height * g->layer ;
	}

	if(o.draw_scale)
		offset += (sblock + 1) * o.scale_height ;

	for(i = 0;i<np;i++) ptmp[i].y = pp[i].y + offset ;

	/* as long as the gene end is in the current block... */
	while(cblock * o.block_length < (g->e - o.min)) {

		if(cblock > sblock) 
			for(i = 0;i<3;i++) ptmp[i].x = o.lmargin ;
		else 
			for(i = 0;i<3;i++) {
				ptmp[i].x = pp[i].x - cblock * o.block_length / o.scale + o.lmargin ;
				if(ptmp[i].x > block_length_scaled + o.lmargin)
					ptmp[i].x = block_length_scaled + o.lmargin ;
				if(ptmp[i].x < o.lmargin) ptmp[i].x = o.lmargin ;
			}
			
		if( (cblock + 1) * o.block_length >= (g->e - o.min)) 
			for(i = 3;i<6;i++) {
				ptmp[i].x = pp[i].x - cblock * o.block_length / o.scale + o.lmargin ;
				if(ptmp[i].x > block_length_scaled + o.lmargin)
					ptmp[i].x = block_length_scaled + o.lmargin ;
				if(ptmp[i].x < o.lmargin) ptmp[i].x = o.lmargin ;
			}
		else
			for(i = 3;i<6;i++) ptmp[i].x = block_length_scaled + o.lmargin ;
			
		if(debug) gp_warn("   d_g_l: gene %i %i %i %i",
			ptmp[1].x,ptmp[2].x,ptmp[3].x,ptmp[4].x) ;

		/* the black outer polygon */

		/* the coordinates of the inner, smaller, filled polygon */
		ptmp2[0].x = ptmp[0].x - o.tline ; ptmp2[0].y = ptmp[0].y - o.tline ;
		ptmp2[1].x = ptmp[1].x - o.tline ; ptmp2[1].y = ptmp[1].y ;
		ptmp2[2].x = ptmp[2].x - o.tline ; ptmp2[2].y = ptmp[2].y + o.tline ;
		ptmp2[3].x = ptmp[3].x + o.tline ; ptmp2[3].y = ptmp[3].y + o.tline ;
		ptmp2[4].x = ptmp[4].x + o.tline ; ptmp2[4].y = ptmp[4].y ;
		ptmp2[5].x = ptmp[5].x + o.tline ; ptmp2[5].y = ptmp[5].y - o.tline ;

		if(ptmp2[1].x < ptmp2[0].x) ptmp2[1].x -= o.tline ;
		if(ptmp2[4].x > ptmp2[4].x) ptmp2[4].x += o.tline ;

		gdImageFilledPolygon(o.image, ptmp2, 6, o.color_values[1]) ;
		gdImageFilledPolygon(o.image, ptmp, 6, g->color) ;

		/* first, the mapfile to standard out: if an url exists */
		if(strlen(g->url) > 1 && o.mapfile) {
			fprintf(stdout,
			"<AREA SHAPE= \"RECT\" HREF= \"%s\" TITLE= \"%s\" ",
				g->url, g->url) ;
			fprintf(stdout, "COORDS= \"%i,%i,%i,%i\">\n",
				ptmp[1].x, ptmp[0].y, ptmp[4].x, ptmp[3].y) ;
		}

		/* only for named genes and when o.shownames defined */
		if(strlen(g->name) > 1 && o.shownames) {
			x = (ptmp[1].x + ptmp[5].x) / 2 ;
			y1 = ptmp[1].y ;
			y2 = y1 + (o.maxlayers - g->layer) * o.layer_height  ;
			y2 += 0.5 * o.font->h + 0.5 * o.gene_height ;
			gdImageFilledRectangle(o.image,x,y1,x+o.nline,y2,o.color_values[1]) ;

			x -= (strlen(g->name) * o.font->w) / 2 ;
			if(x < 0) x = 0 ;
			gdImageString(o.image,o.font,x,y2,g->name,o.color_values[1]) ;
		}

		for(i = 0;i<np;i++) ptmp[i].y += o.block_height + o.scale_height * o.draw_scale ;
		cblock++ ;
	}

	return EXIT_SUCCESS ;
}


/* Draws genes onto the image: preliminary computations */
int draw_gene(gene_s *gene, opt_s o) {

	gdPoint p[6] ;	/* arrow coordinates */
	long arrow, glength, start, end ;

	if(gene->s < o.min) start = 0 ;
	else start = (gene->s - o.min) / o.scale ;

	if(gene->e > o.max) end = (o.max - o.min) / o.scale ;
	else end = (gene->e - o.min) / o.scale ;

	if(gene->type == REGION) {
		p[0].y = p[5].y = - o.large_tick_height ;  
		p[1].y = p[4].y = - o.large_tick_height / 2 ;
		p[2].y = p[3].y = 0 ;
	} else {
		p[0].y = p[5].y = 0 ; 
		p[1].y = p[4].y = o.gene_height / 2 ;
		p[2].y = p[3].y = o.gene_height ;
	}

	glength = end - start ;

	if(gene->type == NORMAL) {
		if(glength < o.gene_height/2) arrow = glength / 2 ;
		else arrow = o.gene_height/2 ;
		if(gene->dir == 1) {   /* from start to end, plus direction */
			p[0].x = p[1].x = p[2].x = start ;
			p[5].x = p[3].x = end - arrow ;
			p[4].x = end ; 
		} else {	/* minus direction */
			p[3].x = p[4].x = p[5].x = end ;
			p[0].x = p[2].x = start + arrow ;
			p[1].x = start ; 
		}
	} else {
		p[0].x = p[1].x = p[2].x = start ;
		p[3].x = p[4].x = p[5].x = end ;
	}

	draw_gene_lowlevel(gene,o,p,6) ;
	return EXIT_SUCCESS ;
}


/* here, a nice scale with ticks and numbering is drawn onto the image */
int draw_scale(opt_s o) {
	int i,j,x,x1,y1,x2,y2, stick, ltick, n_of_blocks ;
	int sblock, block_length_scaled ;
	char tmp[200] ;

	sblock = o.min / o.block_length ;
	stick = o.small_tick / o.scale ;
	ltick = o.large_tick / o.scale ;

	block_length_scaled = o.block_length / o.scale ;
	n_of_blocks = (o.max - 1 )/ o.block_length + 1;

	for(i = sblock ; i< n_of_blocks; i++ ) {

		y1 = y2 = o.tmargin + o.block_height * (i-sblock) + (0.9+i-sblock) * o.scale_height ;
		x1 = o.lmargin ;

		if(i+1 == n_of_blocks) 
			x2 = x1 + (o.max - ( (i - sblock) * o.block_length) - o.min) / o.scale  ;
		else x2 = x1 + block_length_scaled ;

		if(debug)
			gp_warn("draw_scale: block %i from %i to %i (%i to %i)",
				i,i*o.block_length,(i+1)*o.block_length,x1,x2) ;

		gdImageFilledRectangle(o.image,x1,y1-o.tline,x2,y2,o.color_values[1]) ;

		/* large ticks and numbering */
		for(j = 1 ; j< o.block_length ; j += o.large_tick) {
			y2 = y1 - o.large_tick_height ;
			x = o.lmargin + j / o.scale ;
			if(x<x2) {
				gdImageFilledRectangle(o.image,x,y2,x+o.tline,y1,o.color_values[1]) ;
				y2 = o.tmargin + (o.block_height + o.scale_height) * (i-sblock) ;
				sprintf(tmp,"%li",j + (i - sblock) * o.block_length + o.min) ;
				gdImageString(o.image,o.font,x,y2,tmp,o.color_values[1]) ;
			}
		}

		/* small ticks */
		for(j = 1 ; j< o.block_length ; j += o.small_tick) {
			y2 = y1 - o.small_tick_height ;
			x = o.lmargin + j / o.scale ;
			if(x < x2) 
				gdImageFilledRectangle(o.image,x,y2,x+o.nline,y1,o.color_values[1]) ;
		}
	}
	
	return EXIT_SUCCESS ;
}


/* from here, all other picture drawing functions are called */
int draw_picture (gene_s *glist, opt_s o) {
	gene_s *g ;
	int step,i = 0 ;
	char tmp[5],tmp2[65]  ;

	strcpy(tmp2,"                                                              |") ;

	g = glist ;
	step = o.no_of_genes / 60 ;
	if(step<1) step = 1 ;
	if(debug) gp_warn("draw_genes: scale = %i / %i = %f",o.max,o.sizex,o.scale) ;
	if(o.draw_scale) draw_scale(o) ;

	gp_warn("draw_picture: drawing...") ;

	while(g != NULL) {
		i++ ; 
		tmp2[i/step] = ' = ' ;
		if(!quiet && !html && !debug) fprintf(stderr,"\r|%s",tmp2) ;
		
		/*if(debug) gp_warn("draw_picture: drawing gene %i",i) ;*/
		if(g->e >= o.min && g->s <= o.max) draw_gene(g, o) ;
		g = g->next ;
	}

	if(!quiet && !html && !debug) fprintf(stderr,"\n") ;
	return EXIT_SUCCESS ;
}



/* prints all genes one after other to stdout */
int print_gene_list (gene_s *gl, opt_s o) {

	gene_s *g ;
	g = gl ;
	for(g = gl ; g != NULL; g = g->next ) 
		printf("%li %li color %i slot %i name %s\n", 
		g->s, g->e, g->color, g->slot, g->name) ;
	
	return EXIT_SUCCESS ; 
}

/* extract information about the type of the gene from the description string */
int get_gene_type (char *bufor, gene_s *g, opt_s o) {

	char t[20], *types[] = { "normal", "region", "box", "promoter" } ;
	type_s t_n[] = { NORMAL, REGION, BOX, PROMOTER } ;
	int i, n_of_t = 4 ;

	g->type = NORMAL ;
	for(i = 0; i<4 ; i++) {
		strcpy(t,types[i]) ;
		if(strstr(bufor,t) != NULL) {
			g->type = t_n[i] ;
			i = 4 ;
		}
	}

	return EXIT_SUCCESS ; 
}


/* extracts color information about a gene from a string */
int get_gene_color (char * bufor, gene_s * g, opt_s o) {
	char *p, c[51] ;
	int i, found = FALSE ;

	g->color = o.color_values[1] ;

	if( (p = strstr(bufor,"color")) == (char*) NULL) {
		if(debug) gp_warn("get_gene_color: no color found") ;
	} else {
		if(sscanf(p,"color %50s ",c) < 1) 
			gp_warn("get_gene_color: color cannot be read") ;
		else {
			found = FALSE ;

			for(i = 0;i<o.color_number;i++) 
				if(strcmp(c,o.color_names[i]) == 0) {
					g->color = o.color_values[i] ;
					found = TRUE ;
				}

			if(!found) {
				gp_warn("get_gene_color: %s: no such color defined",c) ;
				return EXIT_FAILURE ;
			}
		}
	}

	return EXIT_SUCCESS ;
}


/* extracts name information about a gene from a string */
int get_gene_name (char * bufor, gene_s * g, opt_s o) {
	char *p,format[20] ;

	if( (p = strstr(bufor,"name")) != NULL) {

		if(strlen(p) > MAXSTRING || sscanf(p,"name %s",g->name) < 1) {
			gp_warn("get_gene_name: cannot read %s",bufor) ;
			return EXIT_FAILURE ;
		}
			
	} else g->name[0] = '\0' ;
	return EXIT_SUCCESS ;
}


/* extracts name information about the type of a gene from a string */
/*
int get_gene_type (char * bufor, gene_s * g, opt_s o) {
	char *p, t[50] ;

	g->type = NORMAL ;

	if( (p = strstr(bufor,"type")) != NULL) {
		p = p + strlen("type ") ;

		if(sscanf(p," %50s ",t) < 1) 
			gp_warn("get_gene_type: problems reading gene type") ;

		if(strcmp("promoter",t) == 0) g->type = PROMOTER ;
		if(strcmp("box",t) == 0) g->type = BOX ;
		if(strcmp("region",t) == 0) g->type = REGION ;

	} else g->type = NORMAL ;
	return EXIT_SUCCESS ;
}*/


/* extracts layer information  */
int get_gene_layer (char * u, gene_s * g, opt_s o) {
	char t[BUFSIZ], format[20] ;

	if(sscanf(u,"layer %i",&g->layer) != 1 || g->layer > MAXLAYER - 1) {
		g->layer = 0 ;
		if(debug) gp_warn("Could not process entry: %s (layer > %i ?)",u, MAXLAYER - 1) ;
		return EXIT_FAILURE ;
	}

	return EXIT_SUCCESS ;
}

/* extracts url */
int get_gene_url (char * u, gene_s * g, opt_s o) {
	char t[MAXSTRING], format[20] ;
	sprintf(format,"url %%%is",MAXSTRING-1) ;

	if(sscanf(u,format,g->url) != 1) {
		g->url[0] = '\0' ;
		if(debug) gp_warn("Could not process entry: %s",u) ;
		return EXIT_FAILURE ;
	}

	return EXIT_SUCCESS ;
}


/* extracts data about a gene from a line of the map file */
gene_s * scan_gene(char*bufor, int line, opt_s o) {

	char parameters[BUFSIZ+1], *t ;
	long start, end; 
	gene_s * res ;

	res = NULL ;

	if(strlen(bufor) > BUFSIZ) 
		gp_error_fatal("scan_gene: bufor size too large") ;

	strcpy(parameters,bufor) ;
	if( (t = strstr(parameters,"name")) != NULL)
		t[0] = '\0' ;

	if(sscanf(bufor," %li %li ", &start, &end) < 2 )
		gp_warn("Could not read line %i", line) ;
	else {
		if(start == 0 || end == 0) {
			gp_warn("scan_gene: line %i - gene position cannot have value 0",line) ;
			return NULL ;
		}

		res = malloc(sizeof(*res)) ;
		gene_initialize(res) ;

		if(end > start) { res->s = start ; res->e = end ; res->dir = 1 ; } 
		else { res->s = end ; res->e = start ; res->dir = 0 ; }

		res->layer = 0 ;
		res->next = NULL ;
		res->prev = NULL ;

		get_gene_name(bufor,res,o) ;
		get_gene_color(parameters,res,o) ;
		get_gene_type(parameters,res,o) ;

		if( (t = strstr(parameters,"url")) != NULL && 
			get_gene_url(t,res,o) != EXIT_SUCCESS)
			gp_warn("Problems reading URL information on line %i",line) ;

		if( (t = strstr(parameters,"layer")) != NULL &&
			get_gene_layer(t,res,o) != EXIT_SUCCESS)
			gp_warn("Problems reading layer information on line %i",line) ;

		/*if(debug) gp_warn("gene_s: gene on line %i from %i to %i layer %i",
			line,res->s, res->e,res->layer) ;*/
	}

	return res ; 
}


/* overlapping genes should be a little shifted on the picture, so we can see
 * where one starts and the other ends clearly */
int find_overlaps(gene_s *start, opt_s o) {
	gene_s *cur ;
	int max = 0, finish = FALSE , i,j ;
	long slot[MAXLAYER][42] ; /* max number of overlapping genes is 40 */

	if(debug) gp_warn("Finding overlaps...") ;
	for(j = 0;j<MAXLAYER;j++)
		for(i = 0;i<42;i++) 
			slot[j][i] = 0 ;

	for(cur = start;cur != NULL;cur = cur->next) {
		j = cur->layer ;
		/* looking for a free slot */
		for(i = 0, finish = FALSE;i<42 && !finish ; i++) 
			if(slot[j][i] == 0 || slot[j][i] < cur->s) {
				finish = TRUE ;
				slot[j][i] = cur->e ;
				cur->slot = i ;
				if(max < i) max = i ;
			}
		if(i == 42) gp_error("More then 42 overlapping genes. Cannot handle that 41st") ;

	}
	if(debug) gp_warn("done.");
	return max ;
}


/* finding the maximal overlap */
int find_max_layer(gene_s *start) {
	int res = 0 ;
	gene_s *g ;
	g = start ;
	while(g!= NULL) {
		if(g->layer > res) res = g->layer ;
		g = g->next ;
	}
	if(debug) gp_warn("Maximal layer is %i",res) ;
	return res ;
}

/* sorting a gene into the linked list which starts at "start" */
int fit_into_list(gene_s *start, gene_s *end, gene_s *g) {
	gene_s * cur;
	int finish = FALSE ;

	cur = end ;
	cur->next = g ;
	cur->next->prev = cur ;

	while(!finish && cur->s > cur->next->s) {
		gene_swap(cur,cur->next) ;
		if(cur->prev == NULL) finish = TRUE ;
		else cur = cur->prev ;
	}
	return EXIT_SUCCESS ;
}


/* Reading the list of genes into the linked gene list */
gene_s * read_genes (opt_s o) {
	gene_s * res, * cur, *tmp ;
	int i = 0,l = 0,j = 0 ;
	char bufor[BUFSIZ+1], *p ;
	char d[5] ;

	strcpy(d,"-\\|/") ;

	for(cur = NULL,res = NULL;fgets(bufor,BUFSIZ,o.in) != NULL;l++) {
	
		if(bufor[0] != '\n' && bufor[0] != '#') {

			if((char*) strchr(bufor,'\n') != NULL) {
				p = (char*) strchr(bufor,'\n') ;
				p[0] = '\0' ; 
			}

			tmp = scan_gene(bufor,l,o) ;

			if(tmp != NULL) {
				i++ ;
				if(res == NULL) cur = res = tmp ; 
				else {
					fit_into_list(res,cur,tmp) ;
					cur = cur->next ;
				}
			}
		}
		if(!quiet && !html && !debug) {
			j++ ; if(j>3) j = 0 ;
			fprintf(stderr,"\r%c ",d[j]) ;
		}
	}

	if(!quiet && !html && !debug) fprintf(stderr,"\r") ;
	if(i == 0) gp_error("I have found no genes") ;
	if(debug) gp_warn("%i genes read",i) ;
	return res ;
}


/* set initial values for a gene record */
int gene_initialize(gene_s *g) {
	g->s = 0 ;
	g->e = 0 ;
	g->dir = 0 ;
	g->color = 0 ;
	g->layer = 0 ;
	g->slot = 0 ;
	g->type = NORMAL ;
	g->url[0] = '\0' ;
	g->name[0] = '\0' ;
	return EXIT_SUCCESS ;
}

/* copies a gene from src to dest */
int gene_copy (gene_s *dest, gene_s *src) {

	dest->s = src->s ;
	dest->e = src->e ;
	dest->dir = src->dir ;
	dest->color = src->color ;
	dest->layer = src->layer ;
	dest->slot = src->slot ;
	dest->type = src->type ;
	strcpy(dest->url, src->url) ;
	strcpy(dest->name, src->name) ;
	return EXIT_SUCCESS ;
}


/* swaps two genes */
int gene_swap (gene_s *a, gene_s *b) {

	gene_s tmp ;

	gene_copy(&tmp,a) ;
	gene_copy(a,b) ;
	gene_copy(b,&tmp) ;
	return EXIT_SUCCESS ;
}


/* find the minimal and maximal gene position */
int gene_limits (gene_s * glist, long *min, long *max) {
	gene_s * g;

	*min = glist->s ;
	*max = glist->e ;
	g = glist->next ;

	while(g != NULL ) {
		if(g->s < *min) *min = g->s ;
		if(g->e > *max) *max = g->e ;
		g = g->next ;
	}

	if(debug) gp_warn("gene_limits: from %i to %i", *min, *max) ;
	return EXIT_SUCCESS ; 
}


/* allocate the colors which are stored in the options color array */
int allocate_colors (opt_s * o) {
	int i ;

	for(i = 0;i<o->color_number;i++) {

		o->color_values[i] = gdImageColorAllocate(o->image, 
			o->color_arrays[i][0],
			o->color_arrays[i][1],
			o->color_arrays[i][2]) ;
		
		if(debug) gp_warn("color %s %i %i %i",
			o->color_names[i],
			o->color_arrays[i][0],
			o->color_arrays[i][1],
			o->color_arrays[i][2]) ;
	}
	if(debug) gp_warn("allocate_colors: %i colors allocated") ;
	return EXIT_SUCCESS ;
}


/* read color file into the color array stored in the options variable */
int read_color_file(opt_s * o) {
	char bufor[BUFSIZ+1], tmp[50] ;
	int l,i;

	if(o->color == NULL) return EXIT_FAILURE ;
	i = o->color_number ;

	for(l = 0;fgets(bufor,BUFSIZ,o->color) != NULL && i<256 ;l++) {
		
		if(bufor[0] != '\n' && bufor[0] != '#') {
			if(sscanf(bufor," %i %i %i %20s ",
				&o->color_arrays[i][0],
				&o->color_arrays[i][1],
				&o->color_arrays[i][2],
				o->color_names[i]) < 4)
				gp_warn("read_color_file: could not read color file line %i",l) ;
			else i++ ;
		}
	}

	o->color_number = i ;
	gp_warn("read_color_file: %i colors defined",i) ;
	return EXIT_SUCCESS ;
}


/* read option predefines */
int preset_options (opt_s * o) {

	o->color = NULL ;
	o->config = NULL ;
	o->color_number = 7 ;
	o->bitmap = TRUE ;
	o->mapfile = FALSE ;
	o->shownames = TRUE ;

	o->draw_scale = TRUE ;
	o->sizex = 600 ;
	o->sizey = 400 ;
	o->block_length = 1000 ;
	o->min = 0 ; 
	o->max = 0 ;

	o->font = gdFontMediumBold ;

	o->block_length = 0 ;
	o->large_tick = 1000 ;
	o->small_tick = 100 ;

	/* default colors */
	strcpy(o->color_names[0],"white") ;
	strcpy(o->color_names[1],"black") ;
	strcpy(o->color_names[2],"blue") ;
	strcpy(o->color_names[3],"red") ;
	strcpy(o->color_names[4],"yellow") ;
	strcpy(o->color_names[5],"green") ;
	strcpy(o->color_names[6],"default") ;

	o->color_arrays[0][0] = 255 ;
	o->color_arrays[0][1] = 255 ;
	o->color_arrays[0][2] = 255 ;

	o->color_arrays[1][0] = 0 ;
	o->color_arrays[1][1] = 0 ;
	o->color_arrays[1][2] = 0 ;

	o->color_arrays[2][0] = 0 ;
	o->color_arrays[2][1] = 0 ;
	o->color_arrays[2][2] = 255 ;

	o->color_arrays[3][0] = 255 ;
	o->color_arrays[3][1] = 0 ;
	o->color_arrays[3][2] = 0 ;

	o->color_arrays[4][0] = 255 ;
	o->color_arrays[4][1] = 255 ;
	o->color_arrays[4][2] = 0 ;

	o->color_arrays[5][0] = 0 ;
	o->color_arrays[5][1] = 255 ;
	o->color_arrays[5][2] = 0 ;

	o->color_arrays[6][0] = 255 ;
	o->color_arrays[6][1] = 255 ;
	o->color_arrays[6][2] = 255 ;

	return EXIT_SUCCESS ;
}


/* count the genes stored in a linked list */

int count_genes (gene_s *gg) {
	gene_s *g ;
	int res = 0 ;
	for(g = gg;g != NULL; g = g->next,res++) ;
	return res ;
}


/* automatically set certain image parameters */

int compute_options (opt_s * o) {

	int i,t, xspace, yspace, n_of_blocks ;
	long l ;

	/* 
	 * the image is divide into blocks -- you will not draw 800 000 bp in a single
	 * line, would you? Instead you draw in the first block bases 1..50000, in
	 * the second underneath 50001..100000 etc. You get the picture. If
	 * block_length is undefined, we have to define it first 
	 */

	if(o->block_length > o->max) {
		gp_warn("compute_options: single block length cannot exceed maximal length value") ;
		o->block_length = o->max ;
	}

	if(o->block_length < 0 ) {
		gp_warn("compute_options: block length too small") ;
		o->block_length = 0 ;
	}

	if(o->block_length == 0) {
		l = (o->max - o->min) / 10 ;
		for(o->block_length = 1;
			o->block_length *= 10, l /= 10;l>0) ;
	}

	o->min-- ;

	/* we leave 5% margin on both sides of the picture */
	o->lmargin = 0.05 * o->sizex ;
	o->tmargin = 0.05 * o->sizey ;
	xspace = o->sizex - 2 * o->lmargin ;
	yspace = o->sizey - 2 * o->tmargin ;

	/* the line thickness depends on the image resolution */
	if(o->sizex > o->sizey) t = o->sizex ;
	else t = o-> sizey ;
	o->tline = (t - 500) / 800 + 2 ;
	o->nline = (t - 500) / 1600 + 1 ;
	if(debug) gp_warn("tline %i, nline %i",o->tline,o->nline) ;

	/* predefined values */
	o->large_tick = o->block_length / 10 ;
	o->small_tick = o->large_tick / 10 ;

	if(debug) gp_warn("large tick every %i bp, small tick every %i bp",
		o->large_tick, o->small_tick) ;

	/* block_length is in base pairs. Scale is in basepairs / pixel */
	o->scale = (1.0 * o->block_length ) / (1.0 * xspace) ;
	n_of_blocks = (o->max - o->min - 1) / o->block_length + 1;

	if(debug) gp_warn("resolution %f bp/pixel, block length %i, number of blocks %i",
		o->scale, o->block_length, n_of_blocks) ;

	/* setting spaces for scale, drawing of the genes and names */
	if(o->draw_scale) {
		/* ...depending on the font: */
		o->scale_height = o->font->h * 2 ;

		/* depending on the number of blocks to draw and scaleheight */
		o->block_height = (yspace - o->scale_height * n_of_blocks) / n_of_blocks ;
		if(o->scale_height <= 0 || o->block_height <= 0)
			gp_error("Image Y resolution too small") ;
	} else o->block_height = yspace /  n_of_blocks ;

	/* what part is used by the (a) layer (b) gene itself? */
	o->layer_height = (o->block_height - 3 * o->font->h) / (o->maxlayers + 1) ;

	o->gene_height = 0.9 * o->layer_height / ( 1 + o->maxoverlay / 4 ) ;

	if(debug) gp_warn("height: scale %i, block %i, layer %i, gene %i",
		o->scale_height, o->block_height, o->layer_height, o->gene_height) ;

	o->large_tick_height = 0.33 * o->scale_height ;
	o->small_tick_height = 0.2 * o->scale_height ;

	return EXIT_SUCCESS ;

}

/* Standard mesage */

void Help()
{
printf("\n");
printf("%s, v. %s- no description yet",PROGNAME,VERSION);
printf("\n");
printf("  Usage:\n");
printf("     %s [options] [ input file ] [ output file ]\n",progname);
printf("     %s -i [options] input file output file\n",progname);
printf("\n");
printf("  Options:\n");
printf("     -r <x>x<y>      : resolution e.g. 640x400\n");
printf("     -b value        : single block length in base pairs\n");
printf("     -S              : do not print the gene names on the picture \n");
printf("     -f <t|s|mb|l|g> : use a tiny / small / medium / large / giant font\n");
printf("     -i              : print a html MAP image for use with a CGI script\n");
printf("                       note that it goes to stdout!\n");
printf("     -b value        : set block length to <value> base pairs\n");
printf("     -m value        : start drawing at position <value> base pairs\n");
printf("     -M value        : finish drawing at position <value> base pairs\n");
printf("     -v              : print version information & exit\n");
printf("     -H              : work in HTML mode \n");
printf("     -h              : print this help screen & exit\n");
printf("     -d              : debug mode\n");
printf("     -q              : quiet, suppress error messages\n\n");
exit(0);
}


			
