/* MPMorph - Amiga Morphing program */
/* Copyright (C) © 1993  Topicsave Limited */

/* This program 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 2 of the License, or */
/* any later version. */

/* This program 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 this program; if not, write to the Free Software */
/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

/* mpaddock@cix.compulink.co.uk */

#include "unix.h"

static char *ErrorMessage = NULL;	/* Latest Error Message */

/* Free an image */
extern void
FreeMPImage(struct MPImage *mpi) {
	if (mpi->Red) {
		free(mpi->Red);
	}
	if (!mpi->GreyScale) {
		if (mpi->Green) {
			free(mpi->Green);
		}
		if (mpi->Blue) {
			free(mpi->Blue);
		}
	}
	free(mpi);
}

/* Return the latest error message */
extern char *
MPImageErrorMessage(void) {
	return ErrorMessage;
}

static int ReadPBMChar(FILE *infile) {
	int ch;
	ch = getc(infile);
	if (ch == '#') {
		do {
			ch = getc(infile);
		} while ((ch != '\n') && (ch != EOF));
	}
	return ch;
}

static unsigned int ReadPBMInteger(FILE *infile,int maxval) {
	int ch;
	unsigned int val;
	do {
		ch = ReadPBMChar(infile);
		if (ch == EOF) {
			return 0;
		}
	} while ((ch == ' ') || (ch == '\t') || (ch == '\n') || (ch == '\r'));
	if ((ch < '0') || (ch > '9')) {
		return 0;
	}
	val = (ch - '0');
	while (((ch = ReadPBMChar(infile)) >= '0') && (ch <= '9')) {
		val *= 10;
		val += (ch - '0');
	}
	if (255 == maxval) {
		return val;
	}
	else {
		return ((val * 255)/maxval);
	}
}

/* Load an image */
/* 
 * Input filename of image
 *
 * Returns a struct MPImage
 *
 * 0 on failure (sets up ErrorMessage)
 *
 * MPImage->Red   = chunky red buffer
 *        ->Green = chunky greeen buffer
 *        ->Blue  = chunky blue buffer
 *        ->Width = width of image
 *        ->Height= height of image
 *        ->GreyScale - If true the Red==Green==Blue (image is grey)
 */
extern struct MPImage *
UnixLoadImage(char *filename) {
	FILE *fh;
	char buffer[2];
	struct MPImage *mpi;
	if (mpi = calloc(sizeof(struct MPImage),1)) {
		/* load image */
		if (fh = fopen(filename,"rb")) {
			fread(buffer,2,1,fh);
			if ((buffer[0] == 'P') &&
				 ((buffer[1] == '1') ||
				  (buffer[1] == '4') ||
				  (buffer[1] == '2') ||
				  (buffer[1] == '3') ||
				  (buffer[1] == '5') ||
				  (buffer[1] == '6'))) {
				int width;
				int height;
				int maxval;
				width = ReadPBMInteger(fh,255);
				height = ReadPBMInteger(fh,255);
				if (('1' == buffer[1]) || ('4' == buffer[1])) {
					maxval = 255;
				}
				else {
					maxval = ReadPBMInteger(fh,255);
				}
				if ((width == 0) || (height == 0) || (maxval == 0)) {
					ErrorMessage = "Invalid File Format";
				}
				else {
					if ((buffer[1] == '3') || (buffer[1] == '6')) {
						unsigned char *rr,*gg,*bb;
						int i,j;
						if ((rr = mpi->Red = calloc(width * height,1)) &&
							 (gg = mpi->Green = calloc(width * height,1)) &&
							 (bb = mpi->Blue = calloc(width * height,1))) {
							for (i = 0; i<height; i++) {
								for (j = 0; j<width; j++) {
									switch (buffer[1])  {
									case '3':
										*rr++ = ReadPBMInteger(fh,maxval);
										*gg++ = ReadPBMInteger(fh,maxval);
										*bb++ = ReadPBMInteger(fh,maxval);
										break;
									case '6':
										if (255 == maxval) {
											*rr++ = getc(fh);
											*gg++ = getc(fh);
											*bb++ = getc(fh);
										}
										else {
											*rr++ = (getc(fh)*255)/maxval;
											*gg++ = (getc(fh)*255)/maxval;
											*bb++ = (getc(fh)*255)/maxval;
										}
										break;
									}
								}
							}
							fclose(fh);
							mpi->Width = width;
							mpi->Height = height;
							mpi->GreyScale = TRUE;
							return mpi;
						}
						else {
							if (mpi->Red) {
								free(mpi->Red);
							}
							if (mpi->Green) {
								free(mpi->Green);
							}
							if (mpi->Blue) {
								free(mpi->Blue);
							}
							ErrorMessage = "Out of Memory!";
						}
					}
					else {
						if (mpi->Red = mpi->Green = mpi->Blue = calloc(width * height,1)) {
					   	unsigned char *rr;
					   	int i,j,z;
					   	unsigned char c;
							int currbit = 7;
							rr = mpi->Red;
							switch (buffer[1]) {
							case '5':
								if (255 == maxval) {
							   	fread(rr,width,height,fh);
							   }
							   else {
									for (i = 0; i<height; i++) {
										for (j = 0; j<width; j++) {
											z = getc(fh);
											*rr++ = (z*255)/maxval;
										}
									}
								}
						   	break;
						   case '2':
								for (i = 0; i<height; i++) {
									for (j = 0; j<width; j++) {
										*rr++ = ReadPBMInteger(fh,maxval);
									}
								}
								break;
							case '1':
								for (i = 0; i<height; i++) {
									for (j = 0; j<width; j++) {
										z = ReadPBMInteger(fh,255);
										if (z) {
											*rr++ = 0;
										}
											else {
												*rr++ = 255;
										}
									}
								}
								break;
							case '4':
								c = getc(fh);
								for (i = 0; i<height; i++) {
									for (j = 0; j<width; j++) {
										if ((c>>currbit) & 1) {
											*rr++ = 0;
										}
										else {
											*rr++ = 255;
										}
										if (!currbit) {
											currbit = 7;
											c = getc(fh);
										}
										else {
											--currbit;
										}
									}
								}
								break;
							}
							fclose(fh);
							mpi->Width = width;
							mpi->Height = height;
							mpi->GreyScale = TRUE;
							return mpi;
						}
						else {
							ErrorMessage = "Out of Memory!";
						}
					}
				}
			}
			else {
				ErrorMessage = "Invalid File Format";
			}
			fclose(fh);
		}
		else {
			ErrorMessage = "Can not open file";
		}
		FreeMPImage(mpi);
	}
	else {
		ErrorMessage = "Out of Memory!";
	}
	return 0;
}

/* Scale an MPImage */
/*
 * Input a UnixLoadImage struct MPImage
 *         new width
 *         new height
 *
 * Returns TRUE if OK
 *               mpi should be set to the new size
 *         FALSE on failure
 *               mpi is should still be freeable using FreeMPImage
 *               ErrorMessage should be set up
 *
 * This function need not be implemented. It is not called if
 * the input images are already the correct size
 */
extern BOOL
RescaleMPImage(struct MPImage *mpi,int width,int height) {
	ErrorMessage = "RescaleMPImage not supported, supply images of correct size!";
	return FALSE;
}

/* Save an Image */
/*
 * Input a filename
 *         r,g,b Chunky buffers
 *               (if r==g==b then input is greyscale)
 *         width and height of image
 *
 * Returns TRUE if OK
 *         FALSE on failure (ErrorMessage is set up)
 */
extern BOOL
SaveMPImage(char *filename,UBYTE *r,UBYTE *g,UBYTE *b,int width,int height) {
	FILE *fh;
	char buffer[32];
	int OkFlag = 1;
	if (fh = fopen(filename,"wb")) {
		if ((r == g) && (r == b)) {	// grey scale input
			sprintf(buffer,"P5\n%d %d\n255\n",width,height);
			fputs(buffer,fh);
			if (fwrite(r,width,height,fh) != height) {
				OkFlag = 0;
				ErrorMessage = "Error writing output";
			}
		}
		else {
			UBYTE *arrayr,*arrayg,*arrayb;
			int y,x;
			sprintf(buffer,"P6\n%ld %ld\n255\n",width,height);
			fputs(buffer,fh);
			arrayr=r;
			arrayg=g;
			arrayb=b;
			// loop thru lines
			for (y=0;
				  (y<height) && OkFlag;
					  y++) {
				// Loop thru columns
				for (x=0;
					  (x < width) && OkFlag;
					  x++) {
					if (fputc(*arrayr++,fh) == EOF) {
						OkFlag = 0;
					}
					else {
						if (fputc(*arrayg++,fh) == EOF) {
							OkFlag = 0;
						}
						else {
							if (fputc(*arrayb++,fh) == EOF) {
								OkFlag = 0;
							}
						}
					}
					if (!OkFlag) {
						ErrorMessage = "Error writing output";
					}
				}
			}
		}
		if (fclose(fh)) {
			ErrorMessage = "Error closing output";
			OkFlag = 0;
		}
	}
	else {
		ErrorMessage = "Error opening output";
		OkFlag = 0;
	}
	return OkFlag;
}
