/*
 * Copyright (C) 2011.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 or
 * version 2 as published by the Free Software Foundation.
 *
 * 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.
 */
package uk.me.parabola.imgfmt.app.typ;

import uk.me.parabola.imgfmt.app.BitWriter;
import uk.me.parabola.imgfmt.app.ImgFileWriter;

/**
 * A true colour image.
 *
 * The image is represented by an array of int, with each int in RGBA format.
 *
 * @author Steve Ratcliffe
 */
public class TrueImage implements Image {
	private final ColourInfo colourInfo;
	private final int[] image;
	
	// If this is mode 16, then the transparent colour is set.
	private int transparentPixel;

	public TrueImage(ColourInfo colourInfo, int[] image) {
		analyzeColours(image, colourInfo);
		this.colourInfo = colourInfo;
		this.image = image;
	}

	/**
	 * Write out the image. It is a set of pixel values that are full RGB values, rather than
	 * table driven as in the other image type. If the colour mode is 32 the colours have an
	 * extra 4 bit opacity value following.
	 *
	 * If the colour mode is 16, then the transparent pixel is written just before the image
	 * itself.
	 */
	public void write(ImgFileWriter writer) {
		int width = colourInfo.getWidth();
		int height = colourInfo.getHeight();

		int mode = colourInfo.getColourMode();

		// For mode 16, the transparent pixel precedes the pixmap data.
		if (mode == 16) {
			writer.put1u(transparentPixel>>8);
			writer.put1u(transparentPixel>>16);
			writer.put1u(transparentPixel>>24);
		}

		boolean hasAlpha = mode == 32;

		// Unlike the xpm based images, the true-colour image format does not appear to
		// have any padding so write as a continuous block.
		BitWriter bitWriter = new BitWriter();
		for (int h = 0; h < height; h++) {

			for (int w = 0; w < width; w++) {
				int col = image[h * width + w];

				bitWriter.putn(col>>8 & 0xff, 8);
				bitWriter.putn(col>>16 & 0xff, 8);
				bitWriter.putn(col>>24 & 0xff, 8);

				if (hasAlpha) {
					int alpha = 0xff - (col & 0xff);
					alpha = ColourInfo.alphaRound4(alpha);
					bitWriter.putn(alpha, 4);
				}
			}
		}
		writer.put(bitWriter.getBytes(), 0, bitWriter.getLength());
	}

	/**
	 * Analyze the colours and determine if this should be a mode 16 or 32 image.
	 *
	 * By default it will be a mode 0 image. If there is any transparency the appropriate
	 * colour mode will be selected.
	 *
	 * @param image An images as an array of integers. Each integer is a colour in RGBA format.
	 * @param colourInfo The colour mode will be set in this.
	 */
	private void analyzeColours(int[] image, ColourInfo colourInfo) {
		boolean hasTransparent = false;
		boolean hasAlpha = false;

		int nPixels = colourInfo.getWidth() * colourInfo.getHeight();
		for (int i = 0; i < nPixels; i++) {
			int col = image[i];
			int a = col & 0xff;
			if (a == 0) {
				// Completely transparent, change all transparent pixels to the same value
				if (hasTransparent)
					image[i] = transparentPixel;
				else
					transparentPixel = image[i];
				hasTransparent = true;
			} else if (a < 255) {
				// Partially transparent
				hasAlpha = true;
			}
		}

		if (hasAlpha)
			colourInfo.setColourMode(32);
		else if (hasTransparent)
			colourInfo.setColourMode(16);
	}
}
