#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "mapper_stuff2.h"

typedef struct _int_list_t {
  int val ;
  struct _int_list_t *next ;
} int_list_t ;

typedef struct {
  char *s ;
  int_list_t *il ;
} str_int_list_pair_t ;

typedef struct {
  char *dir ;
  char *prefix ;
  char *name ;
  int arraylen ;
  str_int_list_pair_t **array ;
} converter_info_t ;

static int num_converters = 0 ;
static converter_info_t **converters = NULL ;

typedef struct {
  map_type_t type ;
  char *dir ;
  char *prefix ;
  int height ;
  int width ;
  int num_regs ;
  int *reg_val ;
  int num_colsteps ;
  int *rcol, *gcol, *bcol ;
  int *gradations ;
} map_info_t ;

#define add_new_bit(FP, VAL) { if (bit_index == 0) { cur_byte = getc((FP)) ; bit_index = 128 ; } (VAL) <<= 1 ; if ((cur_byte & bit_index) != 0) { (VAL) |= 1 ; } bit_index >>= 1 ; }

void *map_initialize(char *dir, char *prefix, map_type_t type)
{
  char info_fname[128], rle_fname[128] ;
  struct stat buf1, buf2 ;
  map_info_t *map_info ;
  int i ;
  FILE *fp ;
  char s[80] ;

  sprintf(info_fname, "%s/%s.info", dir, prefix) ;
  sprintf(rle_fname, "%s/%s.rle2", dir, prefix) ;
  if (!stat(info_fname, &buf1) && !stat(rle_fname, &buf2)) {
    map_info = (map_info_t *)malloc(sizeof(map_info_t)) ;
    map_info->type = type ;
    map_info->dir = strdup(dir) ;
    map_info->prefix = strdup(prefix) ;
    fp = fopen(info_fname, "r") ;
    fgets(s, 80, fp) ;
    map_info->height = atoi(s) ;
    fgets(s, 80, fp) ;
    map_info->width = atoi(s) ;
    fgets(s, 80, fp) ;
    fclose(fp) ;
    map_info->num_regs = atoi(s) ;
    map_info->reg_val = (int *)malloc(sizeof(int) * map_info->num_regs) ;
    for (i=0 ; i < map_info->num_regs ; i++)
      map_info->reg_val[i] = 0 ;
    map_info->num_colsteps = 0 ;
    map_info->rcol = map_info->gcol = map_info->bcol = NULL ;
    map_info->gradations = NULL ;
  }
  else map_info = NULL ;
  return((void *)map_info) ;
}

void map_get_dimensions(void *map_info_v, int *height, int *width)
{
  *height = ((map_info_t *)map_info_v)->height ;
  *width = ((map_info_t *)map_info_v)->width ;
}

int map_get_num_regions(void *map_info_v)
{
  return ((map_info_t *)map_info_v)->num_regs ;
}

int conv_qsort_cmp(str_int_list_pair_t **a, str_int_list_pair_t **b)
{
  return strcmp((*a)->s, (*b)->s) ;
}

static int initialize_converter(char *dir, char *prefix, char *name)
{
  char fname[FILENAME_MAX] ;
  FILE *fp ;
  converter_info_t *conv ;
  int count ;
  char s[80] ;
  int_list_t *new_il_elem ;
  int i ;
  

  sprintf(fname, "%s/%s.%s", dir, prefix, name) ;

  fp = fopen(fname, "r") ;

  if (fp != NULL) {

    conv = (converter_info_t *)malloc(sizeof(converter_info_t)) ;
    conv->dir = strdup(dir) ;
    conv->prefix = strdup(prefix) ;
    conv->name = strdup(name) ;
    conv->arraylen = 0 ;
    conv->array = NULL ;

    count = 0 ;
    while (!feof(fp)) {
      fgets(s, 80, fp) ;
      if (!feof(fp)) {
	if (strrchr(s, '\n') != NULL) *strrchr(s, '\n') = '\0' ;
	new_il_elem = (int_list_t *)malloc(sizeof(int_list_t)) ;
	new_il_elem->val = count ;
	for (i=0 ; i < conv->arraylen ; i++)
	  if (!strcmp(conv->array[i]->s, s)) {
	    new_il_elem->next = conv->array[i]->il ;
	    conv->array[i]->il = new_il_elem ;
	    break ;
	  }
	if (i == conv->arraylen) {
	  conv->arraylen++ ;
	  conv->array = (str_int_list_pair_t **)
	    (conv->arraylen == 1 ?
	     malloc(sizeof(str_int_list_pair_t *)) :
	     realloc(conv->array, conv->arraylen * sizeof(str_int_list_pair_t *))) ;

	  conv->array[conv->arraylen-1] = 
	    (str_int_list_pair_t *)malloc(sizeof(str_int_list_pair_t)) ;

	  conv->array[conv->arraylen-1]->s = strdup(s) ;
	  new_il_elem->next = NULL ;
	  conv->array[conv->arraylen-1]->il = new_il_elem ;
	}
	count++ ;
      }
    }
    fclose(fp) ;
    qsort(conv->array, conv->arraylen, sizeof(str_int_list_pair_t *),
	  (int (*)(const void *, const void *))conv_qsort_cmp) ;

    num_converters++ ;
    converters = (converter_info_t **)
      (num_converters == 1 ?
       malloc(sizeof(converter_info_t *)) :
       realloc(converters, 
	       sizeof(converter_info_t *) * num_converters)) ;
    converters[num_converters-1] = conv ;

    return(0) ;
  }
  else return(-1) ;
}

static int_list_t *do_conversion(char *dir, char *prefix, 
				 char *name, char *input_str)
{
  map_info_t *map_info ;
  str_int_list_pair_t *search_ret, **search_ret_p, key, *key_p ;
  int_list_t *retval=NULL ;
  int i ;
  converter_info_t *conv ;

  for (i= 0 ; 
       i < num_converters &&
       !(!strcmp(converters[i]->dir, dir) &&
	 !strcmp(converters[i]->prefix, prefix) &&
	 !strcmp(converters[i]->name, name)) ;
       i++) ;

  if (i == num_converters) 
    if (initialize_converter(dir, prefix, name) == -1) return retval ;

  conv = converters[i] ;

  key.s = input_str ;
  key_p = &key ;
  search_ret_p = bsearch(&key_p, conv->array, conv->arraylen,
		       sizeof(str_int_list_pair_t *), 
		       (int (*)(const void *, const void *))conv_qsort_cmp) ;
  if (search_ret_p != NULL) {
    search_ret = *search_ret_p ;
    retval = search_ret->il ;
  }
  
  return retval ;
}

char *map_convert_region_name(char *dir, char *prefix, 
			      char *scheme1, char *scheme2,
			      char *input_str)
{
  char *retval = NULL ;
  str_int_list_pair_t **search_ret_p, *search_ret, key, *key_p ;
  int_list_t *ilp ;
  converter_info_t *scheme1_conv, *scheme2_conv ;
  int i ;

  for (i=0 ; 
       i < num_converters &&
       !(!strcmp(converters[i]->dir, dir) &&
	 !strcmp(converters[i]->prefix, prefix) &&
	 !strcmp(converters[i]->name, scheme1)) ;
       i++) ;
  if (i == num_converters)
    if (initialize_converter(dir, prefix, scheme1) == -1) return retval ;

  scheme1_conv = converters[i] ;

  for (i=0 ; 
       i < num_converters &&
       !(!strcmp(converters[i]->dir, dir) &&
	 !strcmp(converters[i]->prefix, prefix) &&
	 !strcmp(converters[i]->name, scheme2)) ;
       i++) ;
  if (i == num_converters)
    if (initialize_converter(dir, prefix, scheme2) == -1) return retval ;

  scheme2_conv = converters[i] ;

  key.s = input_str ;
  key_p = &key ;
  search_ret_p = bsearch(&key_p, scheme1_conv->array, scheme1_conv->arraylen,
			 sizeof(str_int_list_pair_t *), 
			 (int (*)(const void *, const void *))conv_qsort_cmp) ;

  if (search_ret_p != NULL) {
    search_ret = *search_ret_p ;
    for (i=0 ; retval == NULL && i < scheme2_conv->arraylen ; i++)
      for (ilp = scheme2_conv->array[i]->il ; 
	   retval == NULL && ilp != NULL ; 
	   ilp = ilp->next)
	if (ilp->val == search_ret->il->val) 
	  retval = scheme2_conv->array[i]->s ;
  }
  return(retval) ;
}

void map_dump_src_url(void *map_info_v, map_xform_t *xform)
{
  int i,j ;
  int max_num_in_reg ;
  int num_gradations, *lower_bound ;
  int multiple, excess, color_step ;
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;

  printf("%s/rle_image_level?fname=%s/%s.rle2&colors=", CGI_BIN_URL,
	 map_info->dir, map_info->prefix) ;
  for (i=0 ; i < map_info->num_colsteps ; i++)
    printf("%.2X%.2X%.2X", map_info->rcol[i], map_info->gcol[i], 
	   map_info->bcol[i]) ;

  if (xform != NULL) {
    if (xform->do_scale)
      printf("&scale=%f", xform->scale_factor) ;
    if (xform->do_clip)
      printf("&left=%d&right=%d&top=%d&bot=%d",
	     xform->clip_left, xform->clip_right, xform->clip_top,
	     xform->clip_bottom) ;
  }
  printf("&array=") ;
  if (map_info->type == ABSOLUTE) 
    for (i=0 ; i < map_info->num_regs ; i++) {
      if (map_info->reg_val[i] < 10)
	putchar('0'+map_info->reg_val[i]) ;
      else if (map_info->reg_val[i] < 36)
	putchar('a'+map_info->reg_val[i]-10) ;
      else putchar('A'+map_info->reg_val[i]-36) ;
  }
  else {
    lower_bound = (int *)malloc(sizeof(int) * (map_info->num_colsteps)) ;

    max_num_in_reg = 0 ;
    for (i=0 ; i < map_info->num_regs ; i++) 
      if (max_num_in_reg < map_info->reg_val[i])
	max_num_in_reg = map_info->reg_val[i] ;

    if (map_info->gradations == NULL) {
      max_num_in_reg = 0 ;
      for (i=0 ; i < map_info->num_regs ; i++) 
	if (max_num_in_reg < map_info->reg_val[i])
	  max_num_in_reg = map_info->reg_val[i] ;

      num_gradations = map_info->num_colsteps - 1 ;
      lower_bound[0] = 1 ;
      if (max_num_in_reg <= num_gradations) {
	for (i=1 ; i < num_gradations ; i++) lower_bound[i] = i+1 ;
	lower_bound[num_gradations] = num_gradations+1 ;
      }
      else {
	multiple = (int)(max_num_in_reg / num_gradations) ;
	excess = max_num_in_reg % num_gradations ;
	for (i=1 ; i < num_gradations ; i++) 
	  lower_bound[i] = 
	    multiple + lower_bound[i-1] + (excess > i-1 ? 1 : 0) ;
	lower_bound[num_gradations] = max_num_in_reg+1 ;
      }
    }
    else {
      num_gradations = map_info->num_colsteps-1 ;
      for (i=0 ; i < map_info->num_colsteps-1 ; i++)
	lower_bound[i] = map_info->gradations[i] ;
      lower_bound[map_info->num_colsteps-1] = max_num_in_reg+1 ;
    }

    for (i=0 ; i < map_info->num_regs ; i++) {
      color_step = 0 ;
      if (map_info->reg_val[i] == -1) 
	color_step = 1 ;
      else 
	for (j=0 ; color_step == 0 && j < num_gradations ; j++)
	  if (map_info->reg_val[i] >= lower_bound[j] &&
	      map_info->reg_val[i] < lower_bound[j+1]) 
	    color_step = j+2 ;
      if (color_step < 10)
	putchar('0'+color_step) ;
      else if (color_step < 36)
	putchar('a'+color_step-10) ;
      else putchar('A'+color_step-36) ;
    }	    
    free(lower_bound) ;
  }
}

void map_output_redirect(void *map_info_v, map_xform_t *xform)
{
  printf("Status: 302 Redirected\n") ;
  printf("Location: ") ;
  map_dump_src_url(map_info_v, xform) ;
  printf("\n\n") ;
}

void get_xformed_dimens(map_info_t *map_info, map_xform_t *xform,
			int *new_rows, int *new_cols)
{
  *new_cols = map_info->width ;
  *new_rows = map_info->height ;
  if (xform != NULL) {
    if (xform->do_scale) {
      *new_cols = *new_cols * xform->scale_factor + 0.999 ;
      *new_rows = *new_rows * xform->scale_factor + 0.999 ;
    }
    if (xform->do_clip) {
      int x0, x1, y0, y1 ;
      if (xform->clip_left < 0) 
	x0 = 0 ;
      else x0 = xform->clip_left ;
      if (xform->clip_top < 0)
	y0 = 0 ;
      else y0 = xform->clip_top ;
      if (xform->clip_right > *new_cols-1)
	x1 = *new_cols-1 ;
      else x1 = xform->clip_right ;
      if (xform->clip_bottom > *new_rows-1) 
	y1 = *new_rows-1 ;
      else y1 = xform->clip_bottom ;
      *new_rows = y1-y0+1 ;
      *new_cols = x1-x0+1 ;
    }
  }
    
}

void map_output_img(void *map_info_v, int ismap, char *alt, char *align,
		    map_xform_t *xform)
{
  map_info_t *map_info ;
  int rows, cols ;

  map_info = (map_info_t *)map_info_v ;

  get_xformed_dimens(map_info, xform, &rows, &cols) ;

  printf("<img height=%d width=%d ", rows, cols) ;
  if (ismap) printf("ismap ") ;
  if (alt != NULL) printf("alt=\"%s\" ", alt) ;
  if (align != NULL) printf("align=\"%s\" ", align) ;
  printf("src=\"") ;
  map_dump_src_url(map_info_v, xform) ;
  printf("\">") ;
}

void map_output_form_elem(void *map_info_v, char *name, char *align,
			  map_xform_t *xform)
{
  map_info_t *map_info ;
  int rows, cols ;

  map_info = (map_info_t *)map_info_v ;

  get_xformed_dimens(map_info, xform, &rows, &cols) ;

  printf("<input type=image height=%d width=%d ", rows, cols) ;
  if (name != NULL) printf("name=\"%s\" ", name) ;
  if (align != NULL) printf("align=\"%s\" ", align) ;
  printf("src=\"") ;
  map_dump_src_url(map_info_v, xform) ;
  printf("\">") ;
}

int map_output_absolute_color_block(void *map_info_v, int color)
{
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;
  if (map_info->type == ABSOLUTE && color > 0 && 
      color <= map_info->num_colsteps) {
    printf("<img src=\"%s/smallsquare?color=%03d%03d%03d\">", CGI_BIN_URL,
	   map_info->rcol[color-1], map_info->gcol[color-1],
	   map_info->bcol[color-1]) ;
    return(0) ;
  }
  else return(-1) ;
}

int map_output_graded_color_key(void *map_info_v)
{
  map_info_t *map_info ;
  int max_num_in_reg, i, num_gradations, *lower_bound, multiple, excess ;
  
  map_info = (map_info_t *)map_info_v ;
  if (map_info->type == GRADED_VALUE) {
    max_num_in_reg = 0 ;
    for (i=0 ; i < map_info->num_regs ; i++) 
      if (max_num_in_reg < map_info->reg_val[i])
	max_num_in_reg = map_info->reg_val[i] ;

    lower_bound = (int *)malloc(sizeof(int) * map_info->num_colsteps) ;
    if (map_info->gradations == NULL) {
      num_gradations = map_info->num_colsteps - 1 ;
      lower_bound[0] = 1 ;
      if (max_num_in_reg <= num_gradations) {
	for (i=1 ; i < num_gradations ; i++) lower_bound[i] = i+1 ;
	lower_bound[num_gradations] = num_gradations+1 ;
      }
      else {
	multiple = (int)(max_num_in_reg / num_gradations) ;
	excess = max_num_in_reg % num_gradations ;
	for (i=1 ; i < num_gradations ; i++) 
	  lower_bound[i] = 
	    multiple + lower_bound[i-1] + (excess > i-1 ? 1 : 0) ;
	lower_bound[num_gradations] = max_num_in_reg+1 ;
      }
    }
    else {
      num_gradations = map_info->num_colsteps-1 ;
      for (i=0 ; i < num_gradations ; i++)
	lower_bound[i] = map_info->gradations[i] ;
      lower_bound[num_gradations] = max_num_in_reg+1 ;
    }

    for (i=0 ; i < num_gradations ; i++) {
      printf("<img src=\"%s/smallsquare?color=%03d%03d%03d\"> %d", CGI_BIN_URL,
	     map_info->rcol[i+1], map_info->gcol[i+1], map_info->bcol[i+1],
	     lower_bound[i]) ;
      if (lower_bound[i+1]-lower_bound[i] > 1) 
	printf("-%d", lower_bound[i+1]-1) ;
      putchar(' ') ;
    }

    free(lower_bound) ;
    return(0) ;
  }
  else return(-1) ;
}

int output_graded_no_data_block(void *map_info_v)
{
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;
  if (map_info->type == GRADED_VALUE) {
    printf("<img src=\"%s/smallsquare?color=192192192\">", CGI_BIN_URL) ;
    return(0) ;
  }
  else return(-1) ;
}

int map_assign_absolute_colors(void *map_info_v,
			   int num_colors,
			   int *reds, int *greens, int *blues)
{
  map_info_t *map_info ;
  int i ;

  map_info = (map_info_t *)map_info_v ;
  if (map_info->type == ABSOLUTE) {
    map_info->num_colsteps = num_colors ;
    map_info->rcol = (int *)malloc(sizeof(int) * num_colors) ;
    map_info->gcol = (int *)malloc(sizeof(int) * num_colors) ;
    map_info->bcol = (int *)malloc(sizeof(int) * num_colors) ;
    for (i=0 ; i < num_colors ; i++) {
      map_info->rcol[i] = reds[i] ;
      map_info->gcol[i] = greens[i] ;
      map_info->bcol[i] = blues[i] ;
    }
    return(0) ;
  }
  else return(-1) ;
}

void map_set_region_color_by_num(void *map_info_v,
				 int reg_num,
				 int color)
{
  ((map_info_t *)map_info_v)->reg_val[reg_num] = color ;
}

void map_set_region_color_by_name(void *map_info_v,
				  char *scheme_name,
				  char *reg_name,
				  int color)
{
  int_list_t *list ;
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;
  list = do_conversion(map_info->dir, map_info->prefix, scheme_name, 
		       reg_name) ;
  while (list != NULL) {
    map_info->reg_val[list->val] = color ;    
    list = list->next ;
  }
}

int map_assign_graded_colors(void *map_info_v,
			 int num_colors,
			 int *reds, int *greens, int *blues)
{
  map_info_t *map_info ;
  int i ;

  map_info = (map_info_t *)map_info_v ;
  if (map_info->type == GRADED_VALUE) {
    map_info->num_colsteps = num_colors+1 ;
    map_info->rcol = (int *)malloc(sizeof(int) * (num_colors+1)) ;
    map_info->gcol = (int *)malloc(sizeof(int) * (num_colors+1)) ;
    map_info->bcol = (int *)malloc(sizeof(int) * (num_colors+1)) ;
    map_info->rcol[0] = 192 ;
    map_info->gcol[0] = 192 ;
    map_info->bcol[0] = 192 ;
    for (i=0 ; i < num_colors ; i++) {
      map_info->rcol[i+1] = reds[i] ;
      map_info->gcol[i+1] = greens[i] ;
      map_info->bcol[i+1] = blues[i] ;
    }
    return(0) ;
  }
  return(-1) ;
}

void map_set_fixed_gradations(void *map_info_v, ...)
{
  va_list arglist ;
  int i ;
  map_info_t *map_info ;
  map_info = (map_info_t *)map_info_v ;
  if (map_info->type == GRADED_VALUE && map_info->num_colsteps > 0) {
    va_start(arglist, map_info_v) ;
    map_info->gradations = (int *)malloc(sizeof(int) * 
					 (map_info->num_colsteps-1));
    for (i=0 ; i < map_info->num_colsteps-1 ; i++)
      map_info->gradations[i] = (int)va_arg(arglist, int) ;
    va_end(arglist) ;
  }
}

void map_add_to_region_by_num(void *map_info_v,
			      int reg_num, int num)
{
  if (((map_info_t *)map_info_v)->reg_val[reg_num] != -1)
    ((map_info_t *)map_info_v)->reg_val[reg_num] += num ;
}

void map_add_to_region_by_name(void *map_info_v,
			       char *scheme_name,
			       char *reg_name,
			       int num)
{
  int_list_t *list ;
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;
  list = do_conversion(map_info->dir, map_info->prefix, scheme_name, 
		       reg_name) ;
  while (list != NULL) {
    map_info->reg_val[list->val] += num ;
    list = list->next ;
  }
}

int map_get_region_val_by_num(void *map_info_v, int reg_num)
{
  return(((map_info_t *)map_info_v)->reg_val[reg_num]) ;

}

int map_get_region_val_by_name(void *map_info_v, char *scheme_name,
			       char *reg_name)
{
  int_list_t *list ;
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;
  list = do_conversion(map_info->dir, map_info->prefix, scheme_name, 
		       reg_name) ;
  return (map_info->reg_val[list->val]) ;
}

int map_disable_all_regions(void *map_info_v)
{
  map_info_t *map_info ;
  int i ;

  map_info = (map_info_t *)map_info_v ;
  if (map_info->type == GRADED_VALUE) {
    for (i=0 ; i < map_info->num_regs ; i++)
      map_info->reg_val[i] = -1 ;
    return(0) ;
  }
  else return(-1) ;
}

int map_enable_region_by_num(void *map_info_v, int reg_num)
{
  ((map_info_t *)map_info_v)->reg_val[reg_num] = 0 ;
}

int map_enable_region_by_name(void *map_info_v, char *scheme_name, 
			      char *reg_name) 
{
  int_list_t *list ;
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;
  list = do_conversion(map_info->dir, map_info->prefix, scheme_name, 
		       reg_name) ;
  while (list != NULL) {
    map_info->reg_val[list->val] = 0 ;
    list = list->next ;
  }
}

int map_disable_region_by_num(void *map_info_v, int reg_num)
{
  ((map_info_t *)map_info_v)->reg_val[reg_num] = -1 ;
}

int map_disable_region_by_name(void *map_info_v, char *scheme_name, 
			       char *reg_name) 
{
  int_list_t *list ;
  map_info_t *map_info ;

  map_info = (map_info_t *)map_info_v ;
  list = do_conversion(map_info->dir, map_info->prefix, scheme_name, 
		       reg_name) ;
  while (list != NULL) {
    map_info->reg_val[list->val] = -1 ;
    list = list->next ;
  }
}

int xy_to_num(int x, int y, char *fname)
{
  FILE *fp ;
  int w1, w2, width ;
  int pos ;
  int region_bits, length_bits ;
  int count, run_length, reg_num ;
  int i ;
  char cur_byte ;
  int bit_index ;

  fp = fopen(fname, "r") ;
  w1 = getc(fp) ; w2 = getc(fp) ; width = (w1 << 8) | w2 ;
  pos = y*width + x ;

  getc(fp) ; getc(fp) ;
  region_bits = getc(fp) ;
  for (length_bits = 0, i = width-2 ; i != 0 ; length_bits++) i >>= 1 ;
  
  cur_byte = bit_index = 0 ;

  count = 0 ;
  do {
    run_length = reg_num = 0 ;
    for (i=0 ; i < region_bits ; i++) add_new_bit(fp, reg_num) ;
    for (i=0 ; i < length_bits ; i++) add_new_bit(fp, run_length) ;
    count += run_length + 1 ;
  } while (count <= pos) ;
  close(fp) ; 
  return(reg_num-1) ;
}
 
int map_convert_xy_to_num(int x, int y, char *dir, char *prefix)
{
  char fname[FILENAME_MAX] ;

  sprintf(fname, "%s/%s.rle2", dir, prefix) ;
  return(xy_to_num(x, y, fname)) ;
}

char *map_convert_xy_to_name(int x, int y, 
			     char *dir, char *prefix, char *scheme)
{
  char fname[FILENAME_MAX] ;
  int num ;
  int i ;
  FILE *fp ;
  char s[80] ;

  sprintf(fname, "%s/%s.rle2", dir, prefix) ;
  num = xy_to_num(x, y, fname) ;
  if (num < 1) return(NULL) ;

  sprintf(fname, "%s/%s.%s", dir, prefix, scheme) ;
  fp = fopen(fname, "r") ;
  i = 0 ;
  do {
    fgets(s, 80, fp) ;
    i++ ;
  } while (i != num) ;
  fclose(fp) ;
  if (strrchr(s, '\n') != NULL) *strrchr(s, '\n') = '\0' ;
  return(strdup(s)) ;
}

typedef struct {
  char *dir ;
  char *prefix ;
  int num_regs ;
  int *reg_val ;
} regarray_t ;

void *regarray_initialize(char *dir, char *prefix)
{
  char info_fname[128] ;
  struct stat buf1 ;
  regarray_t *regarray_info ;
  int i ;
  FILE *fp ;
  char s[80] ;

  sprintf(info_fname, "%s/%s.info", dir, prefix) ;
  if (!stat(info_fname, &buf1)) {
    regarray_info = (regarray_t *)malloc(sizeof(map_info_t)) ;
    regarray_info->dir = strdup(dir) ;
    regarray_info->prefix = strdup(prefix) ;
    fp = fopen(info_fname, "r") ;
    fgets(s, 80, fp) ;
    fgets(s, 80, fp) ;
    fgets(s, 80, fp) ;
    fclose(fp) ;
    regarray_info->num_regs = atoi(s) ;
    regarray_info->reg_val = (int *)malloc(sizeof(int) * 
					   regarray_info->num_regs) ;
    for (i=0 ; i < regarray_info->num_regs ; i++)
      regarray_info->reg_val[i] = 0 ;
  }
  else regarray_info = NULL ;
  return((void *)regarray_info) ;
}

void regarray_set_val_by_name(void *regarray_v, char *scheme,
			      char *name, int val) 
{
  int_list_t *list ;
  regarray_t *regarray ;

  regarray = (regarray_t *)regarray_v ;
  list = do_conversion(regarray->dir, regarray->prefix, scheme, name) ;
  while (list != NULL) {
    regarray->reg_val[list->val] = val ;    
    list = list->next ;
  }

}

void regarray_set_val_by_num(void *regarray_v, int num, int val)
{
  ((regarray_t *)regarray_v)->reg_val[num] = val ;
}

void regarray_add_val_by_name(void *regarray_v, char *scheme,
			      char *name, int val) 
{
  int_list_t *list ;
  regarray_t *regarray ;

  regarray = (regarray_t *)regarray_v ;
  list = do_conversion(regarray->dir, regarray->prefix, scheme, name) ;
  while (list != NULL) {
    regarray->reg_val[list->val] += val ;    
    list = list->next ;
  }

}

void regarray_add_val_by_num(void *regarray_v, int num, int val)
{
  ((regarray_t *)regarray_v)->reg_val[num] += val ;
}


int regarray_get_val_by_name(void *regarray_v, char *scheme,
			      char *name)
{
  int_list_t *list ;
  regarray_t *regarray ;

  regarray = (regarray_t *)regarray_v ;
  list = do_conversion(regarray->dir, regarray->prefix, scheme, name) ;
  if (list != NULL) 
    return regarray->reg_val[list->val] ;
  else return 0 ;
}

int regarray_get_val_by_num(void *regarray_v, int num)
{
  return ((regarray_t *)regarray_v)->reg_val[num] ;
}

int regarray_get_num_regions(void *regarray_v)
{
  return ((regarray_t *)regarray_v)->num_regs ;
}
