/*
 * program epmtoppm - convert Extented Pixmap Format epm into a PPM
 *                    colorizing each layer
 *
 * (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: epmtoppm.c,v 1.1 1999/04/04 11:46:59 xblast Exp $
 * $Log: epmtoppm.c,v $
 * Revision 1.1  1999/04/04 11:46:59  xblast
 * Initial revision
 *
 */

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

#ifndef RGB_TXT
#define RGB_TXT "/usr/lib/X11/rgb.txt"
#endif

struct color {
  unsigned red, green, blue;
};

#ifdef __STDC__
int
parse_color (struct color *col,
	     char *name)
#else
int
parse_color (col, name)
  struct color *col;
  char *name;
#endif
{
  FILE *fp;
  static char line[1024];
  int i, l_len;
  char *ptr;

  /* check if in rgb format */
  if (0 == strncmp(name, "rgb:", 4)) {
    sscanf(name+4,"%x/%x/%x",(&col->red),&(col->green),&(col->blue));
#ifdef DEBUG
    fprintf(stderr, "RGB=%d,%d,%d\n",col->red,col->green,col->blue);
#endif
    switch (strlen(name)) {
    case 9:
      /* 1 digit */
      col->red   = 255*col->red   / 15;
      col->green = 255*col->green / 15;
      col->blue  = 255*col->blue  / 15;
      break;
    case 12:
      /* 2 digits */
      col->red   = col->red;
      col->green = col->green;
      col->blue  = col->blue;
      break;
    case 15:
      /* 3 digits */
      col->red   = col->red   >>4;
      col->green = col->green >>4;
      col->blue  = col->blue  >>4;
      break;
    case 18:
      /* 3 digits */
      col->red   = col->red   >>8;
      col->green = col->green >>8;
      col->blue  = col->blue  >>8;
      break;
    default:
      return 1;
    }
#ifdef DEBUG
    fprintf(stderr, "RGB=%04x,%04x,%04x\n",col->red,col->green,col->blue);
#endif
    return 0;
  }

  if (NULL == (fp = fopen(RGB_TXT, "r" ) ) ) {
    fprintf(stderr, "failed to open rgb file %s\n", RGB_TXT);
    return -1;
  }

  while (! feof(fp) ) {
    /* read one line */
    fgets(line, sizeof(line), fp);
    /* remove trailing end of line */
    l_len = strlen(line)-1;
    line[l_len]='\0';
    /* find start of color name */
    ptr = line;
    /* skip the three rgb values */
    for (; !isdigit(((int)*ptr)); ptr++);
    for (i=0; i<3; i++) {
      for (; isdigit(((int)*ptr)); ptr++);
      for (; !isalnum(((int)*ptr)); ptr++);
    }
    /* compare end of line with color name */
    if (0 == strcasecmp (ptr, name) ) {
      /* get rgb values */
      if (3 != sscanf(line, "%d%d%d", &(col->red), &(col->green), 
		      &(col->blue) ) ) {
	fprintf(stderr, "failed to parse rgb.txt line:\n");
	fprintf(stderr, line);
	continue;
      }
      fclose(fp);
      return 0;
    }
  }
  fclose(fp);
  return 1;
}


#ifdef __STDC__
int 
main (int argc,
      char *argv[])
#else
int 
main (argv, argv)
  int argc;
  char *argv[];
#endif
{
  FILE *fp;
  int width, height, maxval, depth;
  int i,j, layer, n_pixel;
  unsigned char *inbuf, *outbuf;
  struct color *col = NULL;
  char magic[256];
  unsigned red,green,blue;

  /* check args (part one) */
  if ( (argc < 2) || (0 == strcmp(argv[1],"-?") ) ) {
    fprintf(stderr, "usage: %s epmfile color ...\n", argv[0]);
    return (argc != 2);
  }

  /* open file */
  if (0 == strcmp(argv[1],"-")) {
    fp = stdin;
  } else {
    if (NULL == (fp = fopen(argv[1],"r") ) ) {
      fprintf(stderr, "%s: faile to open file %s for reading\n", 
	      argv[0], argv[1]);
      return 1;
    }
  }

  /* read header */
  if (5 != fscanf(fp, "%s%d%d%d%d%*c", magic,&width,&height,&maxval,&depth) ) {
    fprintf(stderr, "%s: Failed to read epm header\n",argv[0]);
    return 1;
  }
  /* test magic */
  if (0 != strcmp(magic,"PX")) {
    fprintf(stderr, "%s: Wrong magic word \"%s\".\n",argv[0],magic);
    return 1;
  }

  /* compare depth oto number of colors given */
  if ((argc - 2) > depth) {
    fprintf(stderr, "%s: warning, to many colors given\n", argv[0]);
  } else if ((argc - 2) < depth) {
    fprintf(stderr, "%s: warning, not enough colors given, using black\n", 
	    argv[0]);
    depth = argc-2;
  }

  /* calc number of pixel per layer */
  n_pixel = width*height;

  /* alloc input and color buffer */
  if ( (NULL == (inbuf  = malloc(depth*n_pixel*sizeof(char) ) ) ) ||
       (NULL == (outbuf = calloc(3*n_pixel,sizeof(char) ) ) ) ||
       (NULL == (col = calloc(depth,sizeof(struct color) ) ) ) ) {
    fprintf(stderr, "%s: Failed to alloc buffer\n",argv[0]);
    return 1;
  }
  
  /* read epm data */
  if (depth*n_pixel != fread(inbuf, sizeof(char), depth*n_pixel, fp) ) {
    perror(argv[0]);
    return 1;
  }

  if (fp != stdin) {
    fclose(fp);
  }

  for (layer=0; layer < depth; layer++) {
    if (parse_color (col+layer, argv[2+layer]) ) {
      fprintf(stderr, "%s: unkown color %s\n", argv[0], argv[2+layer]);
      return 1;
    }
  }

  /* convert pixel data */
  for (i=0, j=0; i<n_pixel; i++, j+=3) {
    red=0;
    green=0;
    blue=0;
    for (layer=0; layer < depth; layer++) {
      red   += col[layer].red   * inbuf[layer*n_pixel+i];
      green += col[layer].green * inbuf[layer*n_pixel+i];
      blue  += col[layer].blue  * inbuf[layer*n_pixel+i];
    }
    if (red > maxval*255) {
      outbuf[j] = 255;
    } else {
      outbuf[j] = red/maxval;
    }
    if (green > maxval*255) {
      outbuf[j+1] = 255;
    } else {
      outbuf[j+1] = green/maxval;
    }
    if (blue > maxval*255) {
      outbuf[j+2] = 255;
    } else {
      outbuf[j+2] = blue/maxval;
    }
  }

  /* write header */
  printf("P6\n");
  printf("%d %d %d\n", width, height, maxval);
  /* write pixel data */
  if (3*n_pixel != fwrite (outbuf, sizeof(char), 3*n_pixel, stdout) ) {
    perror(argv[0]);
    return 1;
  }    

  return 0;
}

