/*
 * program epmarrange - arrange several epm files on a canvas
 *
 * (C) by Oliver Vogel (e-mail: vogel@ikp.uni-koeln.de)
 * April 5th, 1997
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public Licence as by published
 * by the Free Software Foundation; either version 2; or (at your option)
 * any later version
 *
 * This program is distributed in the hope that it will entertaining,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * $Id: epmarrange.c,v 1.1 1999/04/04 11:44:46 xblast Exp $
 * $Log: epmarrange.c,v $
 * Revision 1.1  1999/04/04 11:44:46  xblast
 * Initial revision
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * some type defintions
 */
typedef struct _epm_file {
  unsigned x, y;
  unsigned width, height;
  unsigned maxval, depth;
  unsigned npixel;
  long offset;
  char *fname;
  char *line;
  struct _epm_file * next;
} EpmFile;

typedef struct _epm_data {
  unsigned width, height;
  unsigned maxval, depth;
  unsigned npixel;
  char *layer;
} EpmData;

/*
 * local variables
 */
static char *progname;

/*
 * parse_input: parse the input file for epmfile names and positions
 */
#ifdef __STDC__
static EpmFile *
parse_input (char *filename) 
#else
static EpmFile *
parse_input (filename) 
  char *filename;
#endif
{
  FILE *fp;
  static char line[1024];
  static char epmname[1024];
  unsigned x, y;
  EpmFile *new, *last = NULL, *root = NULL;

  if ( (NULL == filename) || (0 == strcmp (filename, "-") ) ) {
    /* use stdin */
    fp = stdin;
  } else {
    if (NULL == (fp = fopen(filename, "r") ) ) {
      perror(progname);
      return NULL;
    }
  }
  
  while (NULL != fgets(line, sizeof(line), fp) ) {
    if (3 == sscanf(line, "%u %u %s", &x, &y, epmname) ) {
      if ( (NULL == (new = (EpmFile *) malloc(sizeof(EpmFile) ) ) ) ||
	   (NULL == (new->fname = (char *) malloc(strlen(epmname)+1) ) ) ) {
	fprintf(stderr, "%s: alloc failed\n", progname);
	return NULL;
      }
      /* set entries */
      new->x = x;
      new->y = y;
      strcpy(new->fname, epmname);
#ifdef DEBUG
      fprintf(stderr, "EPM %s at (%u,%u)\n", new->fname, new->x, new->y);
#endif
      if (root == NULL) {
	root = last = new;
      } else {
	last->next = new;
	last = new;
      }
    }
  }
  return root;
}

#ifdef __STDC__
static int
check_epm (EpmFile *epm)
#else
static int
check_epm (epm)
  EpmFile *epm;
#endif
{
  FILE *fp;
  char magic[256];

  for (; epm != NULL; epm = epm->next) {
    /* open file */
    if (NULL == (fp = fopen(epm->fname, "r") ) ) {
      perror(progname);
      return 1;
    }
    /* read header */
    if (5 != fscanf(fp, "%s%d%d%d%d%*c", magic, &(epm->width), &(epm->height), 
		    &(epm->maxval), &(epm->depth) ) ) {
      fprintf(stderr, "%s: Failed to read epm header\n",progname);
      return 1;
    }
    /* test magic */
    if (0 != strcmp(magic,"PX")) {
      fprintf(stderr, "%s: Wrong magic word \"%s\".\n", progname, magic);
      return 1;
    }
    /* get offset of pixel data */
    epm->offset = ftell(fp);
    /* close file */
    fclose(fp);
    /* alloc line buffer */
    if (NULL == (epm->line = malloc(epm->width) ) ) {
      fprintf(stderr, "%s: alloc failed\n", progname);
      return 1;
    }
    /* set total number of pixel */
    epm->npixel = epm->width * epm->height;
  }

  return 0;
}


#ifdef __STDC__
static EpmData *
init_epm (EpmFile *epm)
#else
static EpmData *
init_epm (epm)
  EpmFile *epm,
#endif
{
  EpmData *new;
  unsigned w, h;

  if (NULL == (new = calloc(1, sizeof(EpmData) ) ) ) {
    fprintf(stderr, "%s: alloc failed\n", progname);
    return NULL;
  }

  for (; epm != NULL; epm = epm->next) {
    /* new width ? */
    w = epm->x + epm->width;
    if (w > new->width) {
      new->width = w;
    }
    /* new height ? */
    h = epm->y + epm->height;
    if (h > new->height) {
      new->height = h;
    }
    /* new maxval */
    if (epm->maxval > new->maxval) {
      new->maxval = epm->maxval;
    }
    /* new depth */
    if (epm->depth > new->depth) {
      new->depth = epm->depth;
    }
  }
  new->npixel = new->width * new->height;

  /* alloc one layer */
  if (NULL == (new->layer = malloc(new->npixel) ) ) {
    fprintf(stderr, "%s: alloc failed\n", progname);
    return NULL;
  }

  return new;
}

#ifdef __STDC__
static int
draw_one_layer (EpmData *dst,
		EpmFile *src,
		unsigned layer)
#else
static int
draw_one_layer (dst, src, layer)
  EpmData *dst;
  EpmFile *src;
  unsigned layer;
#endif
{
  FILE *fp;
  char *ptr;
  unsigned y;

  /* clear it first */
  memset(dst->layer, 0, src->npixel);

  for (; src != NULL; src = src->next) {
    if (layer < src->depth) {
      /* open src file */
      if (NULL == (fp = fopen(src->fname, "r") ) ) {
	perror(progname);
	return 1;
      }
      /* skip to layer data */
      if (0 > fseek(fp, src->offset + layer * src->npixel, SEEK_SET) ) {
	perror(progname);
	return 1;
      }
      /* draw pixels */
      ptr = dst->layer + src->x + src->y*dst->width;
      for (y=0; y<src->height; y++) {
	/* read line */
	if (src->width != fread (src->line, 1, src->width, fp) ) {
	  if (feof(fp)) {
	    fprintf(stderr, "%s: premature eof in file %s\n",
		    progname, src->fname);
	  } else {
	    perror(progname);
	  }
	  return 1;
	}
	/* copy to dest epm */
	memcpy (ptr, src->line, src->width);
	ptr += dst->width;
      }
      /* close file */
      fclose(fp);
    }
  }
  return 0;
}

#ifdef __STDC__	      
static FILE *
epm_open (EpmData *epm,
	  char *filename)
#else
static FILE *
epm_open (epm, filename)
  EpmData *epm;
  char *filename;
#endif
{
  FILE *fp;

  if ( (NULL == filename) || (0 == strcmp (filename, "-") ) ){
    fp = stdout;
  } else {
    if (NULL == (fp = fopen(filename, "w") ) ) {
      perror(progname);
      return NULL;
    }
  }

  /* write header */
  fprintf(fp,"PX\n");
  fprintf(fp,"%u %u %u %u\n", epm->width,epm->height,epm->maxval,epm->depth);
  
  return fp;
}


#ifdef __STDC__
static int
write_layer (FILE *fp,
	     EpmData *epm)
#else
static int
write_layer (fp, epm)
  FILE *fp;
  EpmData *epm;
#endif
{
  if (epm->npixel != fwrite(epm->layer, 1, epm->npixel, fp) ) {
    perror(progname);
    return 1;
  }

  return 0;
}



/*
 * the main program
 */

#ifdef __STDC__
int
main (int argc,
      char *argv[])
#else
int
main (argc, argv)
  int argc;
  char *argv[];
#endif
{
  char *input  = NULL;
  char *output = NULL;
  EpmFile *epm_list;
  EpmData *new_epm;
  FILE *fp;
  int layer;

  /* parse commandline args */
  switch (argc) {
    /* correct number */
  case 3:
    output   = argv[2];
  case 2:
    input    = argv[1];
  case 1:
    progname = argv[0];
    break;

  default:
    /* print usage message */
    fprintf(stderr, "usage: %s [datafile [outputfile]]\n", argv[0]);
    return 1;
  }
  /* inits */
  if (NULL == (epm_list = parse_input(input) ) ) {
    return 1;
  }
  if (check_epm (epm_list) ){
    return 1;
  }
  if (NULL == (new_epm = init_epm(epm_list) ) ) {
    return 1;
  }
  /* open output file */
  if (NULL == (fp = epm_open(new_epm, output) ) ) {
    return 1;
  }
  for (layer = 0; layer < new_epm->depth; layer++) {
    if (draw_one_layer(new_epm, epm_list, layer) ) {
      return 1;
    }
    if (write_layer(fp, new_epm) ) {
      return 1;
    }
  }
  fclose(fp);

  return 0;
}

