/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

/*
 * JIGL--Java Imaging and Graphics Library
 * Copyright (C)1999 Brigham Young University
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * This library 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
 * Library General Public License for more details.
 * A copy of the GNU Library General Public Licence is contained in
 * /jigl/licence.txt
 */
package jm.jigl; // Modifizierte Paketstruktur [JM]
import jm.jigl.*;
import java.io.*;
import java.net.*;
import java.awt.*;


/**
 * Class declaration
 * @version 1.3, Modifikation der urspruenglichen jigl Paketstruktur durch Jens Martin
 * @see jigl.image.ImageInputStream
 */
public class ImageInputStream extends Canvas {


	/**
	 * InputStream for the Data (Protected)
	 */
	protected BufferedInputStream data;


	/**
	 * Number of dimensions the image has (Protected)
	 */
	protected int									ndims;


	/**
	 * Width of the image (Protected)
	 */
	protected int									X;


	/**
	 * Height of the image (Protected)
	 */
	protected int									Y;


	/**
	 * Image that will be loaded (Protected)
	 */
	protected java.awt.Image			img = null;


	/**
	 * Type of the file: <P>
	 * <DT>UNKNOWN    = 0</DT>
	 * <DT>PGM_ASCII  = 1</DT>
	 * <DT>PGM_ASCII  = 2</DT>
	 * <DT>PPM_ASCII  = 3</DT>
	 * <DT>PBM_RAW    = 4</DT>
	 * <DT>PGM_RAW    = 5</DT>
	 * <DT>PPM_RAW    = 6</DT>
	 * <DT>PRGM_RAW   = 7 </DT>
	 * <DT>PRCM_RAW    = 8 </DT>
	 * <DT>TIFF       = 9 -- not yet implemented</DT>
	 * <DT>JIGL_GRAY  = 10</DT>
	 * <DT>JIGL_COLOR = 11</DT>
	 * <DT>GIFF       = N/A uses Java read-in method and then converted to a JIGL image</DT>
	 * <DT>JPEG       = N/A uses Java read-in method and then converted to a JIGL image</DT>
	 * <DT>JPG        = N/A uses Java read-in method and then converted to a JIGL image </DT>
	 */
	private int										type;
	private static final int			UNKNOWN = 0;
	private static final int			PBM_ASCII = 1;
	private static final int			PGM_ASCII = 2;
	private static final int			PPM_ASCII = 3;
	private static final int			PBM_RAW = 4;
	private static final int			PGM_RAW = 5;
	private static final int			PPM_RAW = 6;
	private static final int			PRGM_RAW = 7;
	private static final int			PRCM_RAW = 8;		// not yet implemented
	private static final int			TIFF = 9;				// not yet implemented
	private static final int			JIGL_GRAY = 10;
	private static final int			JIGL_COLOR = 11;


	/**
	 * Maximum value (integer) of the Image (Protected)
	 */
	protected int									maxval;


	/**
	 * Maximum value (float) of the Image (Protected)
	 */
	protected float								maxvalf;


	/**
	 * Opens a ImageInputStream from a filename.  Please note, GIF, JPG, and JPEG files
	 * are dependant on the filename extension (not on the header).
	 * @param fn the filename to open
	 */
	public ImageInputStream(String fn) throws InterruptedException, FileNotFoundException, ImageNotSupportedException, IOException {

		if (fn.endsWith(".gif") || fn.endsWith(".jpg") || fn.endsWith(".jpeg")) {

			Toolkit toolkit = Toolkit.getDefaultToolkit();

			img = toolkit.getImage(fn);
			MediaTracker	m_tracker = new MediaTracker(this);

			m_tracker.addImage(img, 0);
			m_tracker.waitForAll();
		} else {
			ndims = 2;
			data = new BufferedInputStream(new FileInputStream(fn));
			type = readMagic();
			readHeader();
		}
	}


	/**
	 * Opens a ImageInputStream from a filename
	 * @param the complete URL
	 */
	public ImageInputStream(String url, int i) throws InterruptedException, IOException, ImageNotSupportedException, MalformedURLException {


		URL ur = new URL(url);

		if (url.endsWith(".gif") || url.endsWith(".jpg") || url.endsWith(".jpeg")) {
			Toolkit toolkit = Toolkit.getDefaultToolkit();

			img = toolkit.getImage(ur);
			MediaTracker	m_tracker = new MediaTracker(this);

			m_tracker.addImage(img, 0);
			m_tracker.waitForAll();
		} else {
			ndims = 2;
			data = new BufferedInputStream(ur.openStream());
			type = readMagic();
			readHeader();
		}
	}


	/**
	 * Makes a new ImageInputStream from another ImageInputStream
	 */
	public ImageInputStream(ImageInputStream stream) {
		data = stream.data;
		ndims = stream.ndims;
		X = stream.X;
		Y = stream.Y;
		type = stream.type;
	}


	/**
	 * Returns the number of Dimensions that the image has
	 */
	public int ndims() {
		return ndims;
	}


	/**
	 * Returns the width of the image
	 */
	public int X() {
		return X;
	}


	/**
	 * Returns the height of the image
	 */
	public int Y() {
		return Y;
	}


	/**
	 * Returns the type of image
	 * <DL><DL><DT>UNKNOWN    = 0</DT>
	 * <DT>PGM_ASCII  = 1</DT>
	 * <DT>PGM_ASCII  = 2</DT>
	 * <DT>PPM_ASCII  = 3</DT>
	 * <DT>PBM_RAW    = 4</DT>
	 * <DT>PGM_RAW    = 5</DT>
	 * <DT>PPM_RAW    = 6</DT>
	 * <DT>GIF        = 7 -- not yet implemented</DT>
	 * <DT>JPEG       = 8 -- not yet implemented</DT>
	 * <DT>TIFF       = 9 -- not yet implemented</DT>
	 * <DT>JIGL_GRAY  = 10</DT>
	 * <DT>JIGL_COLOR = 11</DT></DL></DL>
	 */
	public int type() {
		return type;
	}


	/**
	 * Closes the InputStream
	 */
	public void close() throws IOException {
		if (data != null) {
			data.close();
		}
	}


	/**
	 * Gets the magic number and returns the type
	 */
	public int readMagic() throws ImageNotSupportedException, IOException {

		int		t;
		char	c[] = new char[2];

		// get file type magic (first two bytes)

		c[0] = (char) readByte(data);
		c[1] = (char) readByte(data);

		String	str = new String(c);

		// determine type
		if (str.equals("P1")) {
			t = PBM_ASCII;
		} else if (str.equals("P2")) {
			t = PGM_ASCII;
		} else if (str.equals("P3")) {
			t = PPM_ASCII;
		} else if (str.equals("P4")) {
			t = PBM_RAW;
		} else if (str.equals("P5")) {
			t = PGM_RAW;
		} else if (str.equals("P6")) {
			t = PPM_RAW;
		} else if (str.equals("P7")) {
			t = PRGM_RAW;
		} else if (str.equals("P8")) {
			t = PRCM_RAW;
		} else {

			t = UNKNOWN;
			throw new ImageNotSupportedException();
		}

		return t;
	}


	/**
	 * Reads the file header: determines type, size and range of values
	 */
	public void readHeader() throws ImageNotSupportedException, IOException {

		switch (type) {
		case PBM_ASCII:
		case PGM_ASCII:
		case PPM_ASCII:
		case PBM_RAW:
		case PGM_RAW:
		case PPM_RAW:
		case PRGM_RAW:
		case PRCM_RAW:

			// get image dimensions
			X = readInt(data);
			Y = readInt(data);
			ndims = 2;

			// get data range (maximum value)
			if (type != PBM_ASCII && type != PBM_RAW && type != PRGM_RAW && type != PRCM_RAW) {
				maxval = readInt(data);
			} else if (type != PBM_ASCII && type != PBM_RAW) {
				maxvalf = readFloat(data);
			} else {
				maxval = 1;
			}
			break;
		default:
			throw new ImageNotSupportedException();
		}

	}


	/**
	 * Reads in the Image
	 */
	public Image read() throws ImageNotSupportedException, IllegalPBMFormatException, IOException {

		if (img == null) {

			// the different possible image types to return
			GrayImage			g_im = null;
			RealGrayImage rg_im = null;

			// ColorImage     c_im  = null;
			// RealColorImage rc_im = null;
			ComplexImage	cx_im = null;

			switch (type) {
			case PBM_ASCII:
			case PBM_RAW:
			case PGM_RAW:
			case PGM_ASCII:
				g_im = new GrayImage(X, Y);
				break;
			case PPM_ASCII:
			case PPM_RAW:

				// c_im = new ColorImage(X,Y);
				break;
			case PRGM_RAW:
				rg_im = new RealGrayImage(X, Y);
				break;
			case PRCM_RAW:

				// rc_im= new RealColorImage(X,Y);
				break;
			default:
				throw new ImageNotSupportedException();

			}

			int			col, r, g, b;
			float		rf, gf, bf;
			char		c;
			int			value;
			int[]		color = new int[3];
			float[] colorf = new float[3];

			// read image data


			for (int y = 0; y < Y; y++) {
				for (int x = 0; x < X; x++) {
					switch (type) {
					case PBM_ASCII:
						c = readChar(data);
						if (c == '1') {
							value = 0;
						} else if (c == '0') {
							value = 0xff;
						} else {
							throw new IllegalPBMFormatException();

						}
						g_im.set(x, y, value);
						break;
					case PGM_ASCII:
						g = readInt(data);
						g_im.set(x, y, g);
						break;
					case PPM_ASCII:
						r = readInt(data);
						g = readInt(data);
						b = readInt(data);
						color[0] = r;
						color[1] = g;
						color[2] = b;

						// c_im.set(x, y, color);
						break;
					case PBM_RAW:
						if (readBit(data)) {
							value = 0;
						} else {
							value = 0xff;
						}
						g_im.set(x, y, value);
						break;
					case PGM_RAW:
						g = readByte(data);
						if (maxval != 255) {
							g = fixDepth(g);
						}
						g_im.set(x, y, g);
						break;
					case PRGM_RAW:
						gf = readBytef(data);
						if (maxvalf != 255) {
							gf = fixDepth(gf);
						}
						rg_im.set(x, y, gf);
						break;
					case PPM_RAW:
						r = readByte(data);
						g = readByte(data);
						b = readByte(data);
						if (maxval != 255) {
							r = fixDepth(r);
							g = fixDepth(g);
							b = fixDepth(b);
						}
						color[0] = r;
						color[1] = g;
						color[2] = b;

						// c_im.set(x, y, color);
						break;
					case PRCM_RAW:
						rf = readBytef(data);
						gf = readBytef(data);
						bf = readBytef(data);
						if (maxvalf != 255) {
							rf = fixDepth(rf);
							gf = fixDepth(gf);
							bf = fixDepth(bf);
						}
						colorf[0] = rf;
						colorf[1] = gf;
						colorf[2] = bf;

						// rc_im.set(x, y, colorf);
						break;
					}
				}
			}


			// return the right image type
			switch (type) {
			case PBM_ASCII:
			case PBM_RAW:
			case PGM_RAW:
			case PGM_ASCII:
				return g_im;
			case PPM_ASCII:
			case PPM_RAW:

			// return c_im;
			case PRGM_RAW:
				return rg_im;
			case PRCM_RAW:

			// return rc_im;
			default:
				throw new ImageNotSupportedException();

			}
		} else {
			GrayImage image = new GrayImage(img);

			return image;
		}

	}

	// functions imported from JPM library
	//
	// / Utility routine to read a byte.


	/**
	 * Method declaration
	 *
	 *
	 * @param data
	 *
	 * @return
	 *
	 * @throws IOException
	 *
	 * @see
	 */
	private int readByte(InputStream data) throws IOException {
		int b = data.read();

		return b;
	}


	/**
	 * Method declaration
	 *
	 *
	 * @param data
	 *
	 * @return
	 *
	 * @throws IOException
	 *
	 * @see
	 */
	private float readBytef(InputStream data) throws IOException {
		float b = data.read();

		return b;
	}

	// / Utility routine to read an ASCII integer, ignoring comments.


	/**
	 * Method declaration
	 *
	 *
	 * @param data
	 *
	 * @return
	 *
	 * @throws IOException
	 *
	 * @see
	 */
	private int readInt(InputStream data) throws IOException {
		char	c;
		int		i;

		c = readNonwhiteChar(data);

		if (!((c >= '0' && c <= '9') || c == '-')) {
			throw new IOException("junk in file where integer should be");

		}
		i = 0;

		if (c == '-') {
			c = readChar(data);
			do {
				i = i * 10 + c - '0';
				c = readChar(data);
			} while (c >= '0' && c <= '9');
			i = -i;
		} else {
			do {
				i = i * 10 + c - '0';
				c = readChar(data);
			} while (c >= '0' && c <= '9');
		}

		return i;
	}


	/**
	 * Method declaration
	 *
	 *
	 * @param data
	 *
	 * @return
	 *
	 * @throws IOException
	 *
	 * @see
	 */
	private float readFloat(InputStream data) throws IOException {
		char		c;
		float		i, cons;
		boolean flag = true;

		cons = (float) 0.1;
		c = readNonwhiteChar(data);

		if (!((c >= '0' && c <= '9') || c == '-')) {
			throw new IOException("junk in file where integer should be");

		}
		i = 0;

		if (c == '-') {
			c = readChar(data);
			do {
				i = i * 10 + c - '0';
				c = readChar(data);
			} while (c >= '0' && c <= '9');
			i = -i;
			flag = false;
		} else {
			do {
				i = i * 10 + c - '0';
				c = readChar(data);
			} while (c >= '0' && c <= '9');
		}

		c = readChar(data);
		if (c == ('.')) {
			readChar(data);
			do {
				if (flag == true) {
					i = i + cons * (c - '0');
				} else {
					i = i + -(cons * (c - '0'));
				}
				c = readChar(data);
				cons = cons * (float) 0.1;
			} while (c >= '0' && c <= '9');
		}


		return i;
	}

	// / Utility routine to read a character, ignoring comments.


	/**
	 * Method declaration
	 *
	 *
	 * @param data
	 *
	 * @return
	 *
	 * @throws IOException
	 *
	 * @see
	 */
	private char readChar(InputStream data) throws IOException {
		char	c;

		c = (char) readByte(data);
		if (c == '#') {
			do {
				c = (char) readByte(data);
			} while (c != '\n' && c != '\r');
		}

		return c;
	}

	// / Utility routine to read the first non-whitespace character.


	/**
	 * Method declaration
	 *
	 *
	 * @param data
	 *
	 * @return
	 *
	 * @throws IOException
	 *
	 * @see
	 */
	private char readNonwhiteChar(InputStream data) throws IOException {
		char	c;

		do {
			c = readChar(data);
		} while (c == ' ' || c == '\t' || c == '\n' || c == '\r');

		return c;
	}

	// / Utility routine to rescale a pixel value from a non-eight-bit maxval.


	/**
	 * Method declaration
	 *
	 *
	 * @param p
	 *
	 * @return
	 *
	 * @see
	 */
	private int fixDepth(int p) {
		return (p * 255 + maxval / 2) / maxval;
	}


	/**
	 * Method declaration
	 *
	 *
	 * @param p
	 *
	 * @return
	 *
	 * @see
	 */
	private float fixDepth(float p) {
		return (p * 255 + maxvalf / 2) / maxval;
	}

	private int bitshift = -1;
	private int bits;

	// / Utility routine to read a bit, packed eight to a byte, big-endian.


	/**
	 * Method declaration
	 *
	 *
	 * @param in
	 *
	 * @return
	 *
	 * @throws IOException
	 *
	 * @see
	 */
	private boolean readBit(InputStream in) throws IOException {
		if (bitshift == -1) {
			bits = readByte(in);
			bitshift = 7;
		}
		boolean bit = (((bits >> bitshift) & 1) != 0);

		--bitshift;
		return bit;
	}

}



/*--- formatting done in "My Own Convention" style on 08-08-2000 ---*/

