/*

  pixmap -  Decode pixmap
  Copyright (C) 2015, 2018  Filip Hroch, Masaryk University, Brno, CZ

  Rawtran 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 3 of the License, or
  (at your option) any later version.

  Rawtran 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 Rawtran.  If not, see <http://www.gnu.org/licenses/>.

*/


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

#include <assert.h>


/* begin of decode of PGM (grey) or PPM (colour) pixmap file */
/* http://netpbm.sourceforge.net/doc/pgm.html */
/* http://netpbm.sourceforge.net/doc/ppm.html */

int head_num(FILE *f)
{
  const int len = 80;
  int i,c,cycle,anum;
  char l[len+1]; /* len+1 */

  /* skip blanks */
  while( (c = fgetc(f)) != EOF && isspace(c) );
  if( ferror(f) || feof(f) )
    return(-1);

  /* width, height or maxval */
  i = 0;
  cycle= 1;
  while (cycle) {
    l[i] = (unsigned char) c;
    i++;
    l[i] = '\0';
    cycle = (c = fgetc(f)) != EOF && isdigit(c) && i < len;
  }
  if( ferror(f) || feof(f) )
    return(-1);

  if( sscanf(l,"%d",&anum) != 1 )
    return(-1);

  return(anum);
}


PIXMAP *pixmap(FILE *f)
{

  PIXMAP *p;
  int i,j,k,m,lbytes,esize,width,height,maxval,colors,npixels,step;
  const int mlen = 2;
  char magic[mlen+1]; /* mlen + 1 */
  unsigned char *data;
  unsigned short int *pdata;

  /* magic number, recognize pgm and ppm */
  memset(magic,'\0',mlen+1);
  if( fread(magic,mlen,1,f) == 1 ) {
    if( strcmp(magic,"P5") == 0 )
      colors = 1;
    else if ( strcmp(magic,"P6") == 0 )
      colors = 3;
    else
      return(NULL);
  }
  else
    return(NULL);

  /* width, height, max value */
  width = head_num(f);
  height = head_num(f);
  maxval = head_num(f);
  /* fprintf(stderr,"%d %d %d %d\n",width,height,maxval,colors); */
  if( width <= 0 || height <= 0 || !(0 < maxval && maxval < 65536) ) {
    return(NULL);
  }

  /* PGM structure */
  p = malloc(sizeof(PIXMAP));
  p->width = width;
  p->height = height;
  p->maxval = maxval;
  p->colors = colors;
  if( (p->data = malloc(width*height*colors*sizeof(short))) == NULL ) {
    free(p);
    return(NULL);
  }
  pdata = p->data;

  /* identify data */
  if( maxval < 256 )
    esize = 1;
  else
    esize = 2;

  /* load image data */
  lbytes = width*esize*colors;
  npixels = width*height;
  step = esize*colors;
  data = malloc(lbytes);
  for( i = 0; i < height; i++) {

    if( fread(data,1,lbytes,f) != lbytes ) {
      free(p->data);
      free(p);
      return(NULL);
    }

    k = width*(height - i - 1);

    if( esize == 1 ) {

      for( j = 0; j < lbytes; j=j+step, k++ )
	for( m = 0; m < colors; m++)
	  pdata[k+m*npixels] = data[j+(colors - 1 - m)];

    }
    else {

      unsigned char *d = data;
      unsigned short int *p = pdata + k - 1;
      for( j = 0; j < width; j++ ) {
	p++;
	for( m = (colors-1)*npixels; m >= 0; m -= npixels){
	  p[m] = 256 * *d++;
	  p[m] += *d++;
	}
      }
      k += width;
    }

  }
  free(data);
  return(p);
}

PIXMAP *pixmap_init(int width, int height, int maxval, int colors)
{
  PIXMAP *p;

  p = malloc(sizeof(PIXMAP));
  p->width = width;
  p->height = height;
  p->maxval = maxval;
  p->colors = colors;

  if( (p->data = malloc(width*height*colors*sizeof(short))) == NULL ) {
    perror("pixmap_init");
    free(p);
    return(NULL);
  }
  return(p);
}

void pixmap_free(PIXMAP *p)
{
  free(p->data);
  free(p);
  p = NULL;
}
/* end of decode of PIXMAP file */


void pixmap_save(PIXMAP *p, char *filename)
{
  FILE *file;
  int esize,n,i,j,nbytes;
  unsigned char *data;
  unsigned short int *pdata;

  if( (file = fopen(filename,"w")) == NULL )
    perror(filename);

  if( p->colors == 1 )
    fprintf(file,"P5\n");
  else if( p->colors == 3 )
    fprintf(file,"P6\n");

  fprintf(file,"%d %d\n",p->width,p->height);
  fprintf(file,"%d\n",p->maxval);

  /* identify data */
  if( p->maxval < 256 )
    esize = 1;
  else
    esize = 2;

  nbytes = esize*p->width;
  data = malloc(nbytes);
  for( i = 0; i < p->height; i++) {
    pdata = p->data + p->width * i;

    if( esize == 2 ) {
      for( n = 0, j = 0; j < p->width; j++) {
	data[n] = pdata[j] / 256;
	data[n+1] = pdata[j] % 256;
	n = n + 2;
      }
    }
    else if( esize == 1 ) {
      for( j = 0; j < p->width; j++)
	data[j] = pdata[j];
    }

    fwrite(data,1,nbytes,file);
  }
  free(data);
  fclose(file);
}


int pixmap_ppm2pgm(char *ppmname, char* pgmname)
{
  FILE *ppmfile;
  PIXMAP *ppm, *pgm;
  unsigned short int *pgmdata, *ppmdata;
  int j,i,n;

  if( (ppmfile = fopen(ppmname,"r")) == NULL ) {
    fprintf(stderr,"Error: Failed to open `%s'.\n",ppmname);
    return(1);
  }
  ppm = pixmap(ppmfile);
  fclose(ppmfile);

  pgm = pixmap_init(ppm->width,ppm->height,ppm->maxval,1);
  for( j = 0; j < ppm->height; j++) {
    ppmdata = ppm->data + ppm->colors * ppm->width * j;
    pgmdata = pgm->data + pgm->width * j;

    n = 0;
    for( i = 0; i < pgm->width; i++) {
      pgmdata[i] = 0.2989*ppmdata[n] + 0.5866*ppmdata[n+1] + 0.1145*ppmdata[n+2];
      n = n + 3;
    }
  }

  pixmap_save(pgm,pgmname);
  pixmap_free(pgm);
  pixmap_free(ppm);

  return(0);
}
