package ar.com.hjg.pngj.chunks;

import java.io.ByteArrayInputStream;

import ar.com.hjg.pngj.ImageInfo;
import ar.com.hjg.pngj.PngHelperInternal;
import ar.com.hjg.pngj.PngjException;
import ar.com.hjg.pngj.PngjInputException;

/**
 * IHDR chunk.
 * <p>
 * see http://www.w3.org/TR/PNG/#11IHDR
 * <p>
 * This is a special critical Chunk.
 */
public class PngChunkIHDR extends PngChunkSingle {
	public final static String ID = ChunkHelper.IHDR;

	private int cols;
	private int rows;
	private int bitspc;
	private int colormodel;
	private int compmeth;
	private int filmeth;
	private int interlaced;

	// http://www.w3.org/TR/PNG/#11IHDR
	//
	public PngChunkIHDR(final ImageInfo info) { // argument is normally null here, if not null is used to fill the fields
		super(PngChunkIHDR.ID, info);
		if (info != null)
			fillFromInfo(info);
	}

	@Override
	public ChunkOrderingConstraint getOrderingConstraint() {
		return ChunkOrderingConstraint.NA;
	}

	@Override
	public ChunkRaw createRawChunk() {
		final ChunkRaw c = new ChunkRaw(13, ChunkHelper.b_IHDR, true);
		int offset = 0;
		PngHelperInternal.writeInt4tobytes(cols, c.data, offset);
		offset += 4;
		PngHelperInternal.writeInt4tobytes(rows, c.data, offset);
		offset += 4;
		c.data[offset++] = (byte) bitspc;
		c.data[offset++] = (byte) colormodel;
		c.data[offset++] = (byte) compmeth;
		c.data[offset++] = (byte) filmeth;
		c.data[offset++] = (byte) interlaced;
		return c;
	}

	@Override
	public void parseFromRaw(final ChunkRaw c) {
		if (c.len != 13)
			throw new PngjException("Bad IDHR len " + c.len);
		final ByteArrayInputStream st = c.getAsByteStream();
		cols = PngHelperInternal.readInt4(st);
		rows = PngHelperInternal.readInt4(st);
		// bit depth: number of bits per channel
		bitspc = PngHelperInternal.readByte(st);
		colormodel = PngHelperInternal.readByte(st);
		compmeth = PngHelperInternal.readByte(st);
		filmeth = PngHelperInternal.readByte(st);
		interlaced = PngHelperInternal.readByte(st);
	}

	public int getCols() {
		return cols;
	}

	public void setCols(final int cols) {
		this.cols = cols;
	}

	public int getRows() {
		return rows;
	}

	public void setRows(final int rows) {
		this.rows = rows;
	}

	public int getBitspc() {
		return bitspc;
	}

	public void setBitspc(final int bitspc) {
		this.bitspc = bitspc;
	}

	public int getColormodel() {
		return colormodel;
	}

	public void setColormodel(final int colormodel) {
		this.colormodel = colormodel;
	}

	public int getCompmeth() {
		return compmeth;
	}

	public void setCompmeth(final int compmeth) {
		this.compmeth = compmeth;
	}

	public int getFilmeth() {
		return filmeth;
	}

	public void setFilmeth(final int filmeth) {
		this.filmeth = filmeth;
	}

	public int getInterlaced() {
		return interlaced;
	}

	public void setInterlaced(final int interlaced) {
		this.interlaced = interlaced;
	}

	public boolean isInterlaced() {
		return getInterlaced() == 1;
	}

	public void fillFromInfo(final ImageInfo info) {
		setCols(imgInfo.cols);
		setRows(imgInfo.rows);
		setBitspc(imgInfo.bitDepth);
		int colormodel = 0;
		if (imgInfo.alpha)
			colormodel += 0x04;
		if (imgInfo.indexed)
			colormodel += 0x01;
		if (!imgInfo.greyscale)
			colormodel += 0x02;
		setColormodel(colormodel);
		setCompmeth(0); // compression method 0=deflate
		setFilmeth(0); // filter method (0)
		setInterlaced(0); // we never interlace
	}

	/** throws PngInputException if unexpected values */
	public ImageInfo createImageInfo() {
		check();
		final boolean alpha = (getColormodel() & 0x04) != 0;
		final boolean palette = (getColormodel() & 0x01) != 0;
		final boolean grayscale = getColormodel() == 0 || getColormodel() == 4;
		// creates ImgInfo and imgLine, and allocates buffers
		return new ImageInfo(getCols(), getRows(), getBitspc(), alpha, grayscale, palette);
	}

	public void check() {
		if (cols < 1 || rows < 1 || compmeth != 0 || filmeth != 0)
			throw new PngjInputException("bad IHDR: col/row/compmethod/filmethod invalid");
		if (bitspc != 1 && bitspc != 2 && bitspc != 4 && bitspc != 8 && bitspc != 16)
			throw new PngjInputException("bad IHDR: bitdepth invalid");
		if (interlaced < 0 || interlaced > 1)
			throw new PngjInputException("bad IHDR: interlace invalid");
		switch (colormodel) {
			case 0:
				break;
			case 3:
				if (bitspc == 16)
					throw new PngjInputException("bad IHDR: bitdepth invalid");
				break;
			case 2:
			case 4:
			case 6:
				if (bitspc != 8 && bitspc != 16)
					throw new PngjInputException("bad IHDR: bitdepth invalid");
				break;
			default:
				throw new PngjInputException("bad IHDR: invalid colormodel");
		}
	}

}
