/* pnmwrite.c: write a pnm file */

#include "pnm.h"
#include "pnm_private.h"
#include "pools.h"

#ifndef RAW_PNM
/* writes ascii pbm */
static int WritePBM(IMAGE *pnm, FILE *fp)
{
  int row, col, count;

  fprintf(fp, "P1\n");
  fprintf(fp, "%ld %ld\n", pnm->width, pnm->height);
  
  count = 0;
  for (row = 0; row < pnm->height; row++)
    for (col = 0; col < pnm->width; col++) {
      if (pnm->pix[0][row][col] == 0)
	fputc('1', fp);	/* black */
      else
	fputc('0', fp); /* white */
      fputc(' ', fp);
      count++;
      
      if ((count % 32) == 0)	/* max. 64 < 70 characters per line */
	fputc('\n', fp);
    }
  
  return 0;
}	
#endif /*RAW_PNM*/

/* writes ascii pgm */
static int WritePGM(IMAGE *pnm, FILE *fp)
{
  int row, col, n, count;
  
  fprintf(fp, "P2\n");
  fprintf(fp, "%ld %ld\n", pnm->width, pnm->height);
  fprintf(fp, "%ld\n", pnm->maxval);
  
  count = 0;
  for (row = 0; row < pnm->height; row++)
    for (col = 0; col < pnm->width; col++) {
      fprintf(fp, "%3d %n", pnm->pix[0][row][col], &n);
      count += n;
      if (count > 64) {
	fputc('\n', fp);
	count = 0;
      }
    }
  
  return 0;
}

/* writes ascii ppm */
static int WritePPM(IMAGE *pnm, FILE *fp)
{
  int row, col, n, count;
  
  fprintf(fp, "P3\n");
  fprintf(fp, "%ld %ld\n", pnm->width, pnm->height);
  fprintf(fp, "%ld\n", pnm->maxval);
  
  count = 0;
  for (row = 0; row < pnm->height; row++)
    for (col = 0; col < pnm->width; col++) {
      fprintf(fp, "%3d %3d %3d  %n", 
	      pnm->pix[0][row][col], /* red */
	      pnm->pix[1][row][col], /* green */
	      pnm->pix[2][row][col], /* blue */
	      &n); count += n;
	      if (count > 50) {
		fputc('\n', fp);
		count = 0;
	      }
    }
  
  return 0;
}

/* writes raw pbm */
static int WriteRawPBM(IMAGE *pnm, FILE *fp)
{
  int row, col, n, c;
  
  fprintf(fp, "P4\n");
  fprintf(fp, "%ld %ld\n", pnm->width, pnm->height);
  
  n = 0; c = 0;
  for (row = 0; row < pnm->height; row++)
    for (col = 0; col < pnm->width; col++) {
      c <<= 1; n++;
      c |= !(pnm->pix[0][row][col] & 1);
      
      if ((n % 8) == 0) {
	fputc(c, fp);	
	c = 0;
      }
    }
  
  return 0;
}

/* writes raw pgm */
static int WriteRawPGM(IMAGE *pnm, FILE *fp)
{
  int row, col;
  
  fprintf(fp, "P5\n");
  fprintf(fp, "%ld %ld\n", pnm->width, pnm->height);
  fprintf(fp, "%ld\n", pnm->maxval);
  
  for (row = 0; row < pnm->height; row++)
    for (col = 0; col < pnm->width; col++) 
      fputc(pnm->pix[0][row][col], fp);
  
  return 0;
}

/* writes raw ppm */
static int WriteRawPPM(IMAGE *pnm, FILE *fp)
{
  int row, col;
  
  fprintf(fp, "P6\n");
  fprintf(fp, "%ld %ld\n", pnm->width, pnm->height);
  fprintf(fp, "%ld\n", pnm->maxval);
  
  for (row = 0; row < pnm->height; row++)
    for (col = 0; col < pnm->width; col++) {
      fputc(pnm->pix[0][row][col], fp);
      fputc(pnm->pix[1][row][col], fp);
      fputc(pnm->pix[2][row][col], fp);
    }
  
  return 0;
}

/*
 * Write a pnm-file:
 *
 * If the number of channels is 1 then 
 *	If the max. gray value is 1 then 
 *		a raw pbm file is written
 *	else if  the max. gray value <= 255 then
 *		a raw pgm file is written
 *	else
 *		an ascii pgm file is written
 * else if the number of channels is 3 then
 *	if the max. color value <= 255 then
 *		a raw ppm file is written
 *	else
 *		an ascii ppm file is written
 * else
 *	an error message is written to stderr 
 *
 * Input: pointer to a filled in IMAGE struct.
 *	  pointer to the file to be written.
 *
 * Output: 0 if succesful, nonzero if not.
 */ 
int PnmWrite(IMAGE *pnm, FILE *fp)
{
  switch (pnm->nrchannels) {
  case 1:
    if (pnm->maxval == 1)
#ifndef RAW_PNM
      return WritePBM(pnm, fp);
#else /*RAW_PNM*/
    return WriteRawPBM(pnm, fp);
    else if (pnm->maxval <= 255)
      return WriteRawPGM(pnm, fp);
#endif /*RAW_PNM*/
    else 
      return WritePGM(pnm, fp);
  case 3:
#ifdef RAW_PNM
    if (pnm->maxval <= 255)
      return WriteRawPPM(pnm, fp);
    else
#endif /*RAW_PNM*/
      return WritePPM(pnm, fp);
  default:
    PnmError(NULL, "Can't write pnm file with number of channels not equal to 1 or 3");
    return 1;
  }

  return 0;
}




