/* +-------------------------------------------------------------------+ */
/* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)	       | */
/* |								       | */
/* | Permission to use, copy, modify, and to distribute this software  | */
/* | and its documentation for any purpose is hereby granted without   | */
/* | fee, provided that the above copyright notice appear in all       | */
/* | copies and that both that copyright notice and this permission    | */
/* | notice appear in supporting documentation.	 There is no	       | */
/* | representations about the suitability of this software for	       | */
/* | any purpose.  this software is provided "as is" without express   | */
/* | or implied warranty.					       | */
/* |								       | */
/* +-------------------------------------------------------------------+ */

/* $Id: readWritePNM.c,v 1.21 2005/03/20 20:15:34 demailly Exp $ */

#include <stdio.h>
#include <string.h>
#ifdef NETPBM11
#include <netpbm/pam.h>
#else
#include <pam.h>
#endif

#include "image.h"

#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include "xpaint.h"

Image *
ReadPNM(char *file)
{
    int x, y, comp;
    FILE *fd;
    Image *image;
    unsigned char *data, *alpha;
    struct pam inpam;
    tuple * tuplerow;
    char *i, *o;

    pm_init("xpaint", 0);
    fd = pm_openr(file);
    
#ifdef PAM_STRUCT_SIZE
    pnm_readpaminit(fd, &inpam, PAM_STRUCT_SIZE(tuple_type));
#else
    pnm_readpaminit(fd, &inpam, sizeof(struct pam));
#endif
#if DEBUG    
    printf("%s : depth %d maxval %d\n", file, inpam.depth, inpam.maxval);
#endif
    
    tuplerow = pnm_allocpamrow(&inpam);

    if (inpam.depth <= 2)
        image = ImageNewGrey(inpam.width, inpam.height);
    else
        image = ImageNew(inpam.width, inpam.height);      

    data = image->data;
    if (inpam.depth == 4)
        alpha  = malloc(inpam.width * inpam.height);
    else
        alpha = NULL;
    image->alpha = alpha;
    
    for (y = 0; y < inpam.height; ++y) {
        pnm_readpamrow(&inpam, tuplerow);
        for (x = 0; x < inpam.width; ++x) {
            if (inpam.depth <= 2)
	        *data++ = tuplerow[x][0] * 255 / inpam.maxval;
	    else
            for (comp = 0; comp < 3; ++comp) {
                *data++ = tuplerow[x][comp] * 255 / inpam.maxval;
            }
            if (alpha)
	        *alpha++ = tuplerow[x][3];
        }
    }
    pnm_freepamrow(tuplerow);
    /* test if there are further unread data in the file,
       and interpret them as a potential alpha channel */
    if (!alpha && !feof(fd)) {
        x = 0 ;
	y = inpam.width * inpam.height;
        alpha  = data = malloc(y);
	while (!feof(fd) && x < y) {
	    *data++ = fgetc(fd);
	    ++x;
	}
	/* if not enough data, remove alpha channel */
	if (x < y)
	    free(alpha);
	else
	    image->alpha = alpha;
    }
    pm_closer(fd);
    
    return image;
}

static int WritePNMtoFD(FILE *fd, Image * image, int mode)
{
    int x, y, i, grey;
#ifdef NETPBM11
    int alphacomp;
#endif    
    struct pam outpam;
    tuple * tuplerow;
    unsigned char *data, *alpha;
    
    if (!fd) return 1;

    grey = image->isGrey;
    alpha = image->alpha;

    /* mode = 4 puts alpha channel, if any, at the end of a RGB/Gray PPM */
#ifdef NETPBM11    
    alphacomp = alpha && (!(mode & 4));
    
    if (grey) {
	if (alphacomp)
	    outpam.depth = 2;
	else
	    outpam.depth = 1;	  
    } else {
        if (alphacomp) {
	    alphacomp = 3;
	    outpam.depth = 4;
	} else
	    outpam.depth = 3;	  
    }
    
    if (alphacomp) {
        outpam.format = PAM_FORMAT;  
        if (grey)
	    strcpy(outpam.tuple_type, PAM_PGM_ALPHA_TUPLETYPE);
	else
	    strcpy(outpam.tuple_type, PAM_PPM_ALPHA_TUPLETYPE);
    } else {
        if (grey)
            outpam.format = PGM_FORMAT;
	else
            outpam.format = PPM_FORMAT;
        outpam.plainformat = mode & 1;
    }
#else
    if (grey)
        outpam.depth = 1;
    else
	outpam.depth = 3;
#endif
    
    outpam.size = sizeof(outpam);
#ifdef PAM_STRUCT_SIZE
    outpam.len = PAM_STRUCT_SIZE(tuple_type);
#else
    outpam.len = sizeof(struct pam),
#endif
    outpam.file = fd;
    outpam.width = image->width;
    outpam.height = image->height;
    outpam.maxval = 255;
    
    pnm_writepaminit(&outpam);
    tuplerow = pnm_allocpamrow(&outpam);
    
    for (y = 0; y < image->height; y++) {
	for (x = 0; x < image->width; x++) {
	    data = ImagePixel(image, x, y);
	    if (grey)
	        tuplerow[x][0] = *data++;
	    else {
	        for (i=0; i<3; i++)
		    tuplerow[x][i] = *data++;
	    }
#ifdef NETPBM11	    
	    if (alphacomp)
	        tuplerow[x][alphacomp] = *alpha++;
#endif	    
	}
	pnm_writepamrow(&outpam, tuplerow);
    }
    pnm_freepamrow(tuplerow);

    /* if alpha exists but mode == 4, put alpha channel at the end */
#if NETPBM11    
    if (alpha && !alphacomp) {
#else
    if (alpha) {
#endif
        for (y = 0; y < image->height; y++)
	    for (x = 0; x < image->width; x++)
	        fputc((int)*alpha++, fd);
    }
    /* mode & 2 non zero is for concatenating several PPM files 
       so, do not close file */
    fflush(fd);
    if (mode&2 == 0) pm_closew(fd);
    return 0;
}

int WritePNM(char *file, Image * image)
{
    FILE * fd;
    pm_init("xpaint", 0);
    fd = pm_openw(file);      
    return WritePNMtoFD(fd, image, Global.image_param.pnm_ascii);
}

int WriteBinaryPNM(char *file, Image * image)
{
    FILE * fd;
    pm_init("xpaint", 0);
    fd = pm_openw(file);      
    return WritePNMtoFD(fd, image, 0);
}

int WriteAlphaPNM(char *file, Image * image)
{
    FILE * fd;
    pm_init("xpaint", 0);
    fd = pm_openw(file);
    /* write alpha channel, if any, after PPM file */
    return WritePNMtoFD(fd, image, 4);
}

int WriteAsciiPNM(char *file, Image * image)
{
    FILE * fd;
    pm_init("xpaint", 0);
    fd = pm_openw(file);      
    return WritePNMtoFD(fd, image, 1);
}

int WriteAsciiPNMtoFD(FILE * fd, Image * image)
{
    pm_init("xpaint", 0);
    return WritePNMtoFD(fd, image, 3);
}

int TestPNM(char *file)
{
    FILE *fd;
    int ret = 0;
    char buf[4];

    if ((fd = fopen(file, "r")) == NULL)
	return 0;

    if (fread(buf, sizeof(char), 3, fd) == 3) {
	if (buf[0] == 'P') {
	    if (buf[1] >= '1' && buf[1] <= '7')
	        ret = 1;
	}
    }
    fclose(fd);

    return ret;
}
