/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.graphics;

import org.eclipse.swt.*;

import java.io.*;

import intrinsic.Function;
import intrinsic.flash.display.BitmapData;
import intrinsic.flash.display.LoaderInfo;
import intrinsic.flash.system.LoaderContext;
import intrinsic.flash.utils.*;
import intrinsic.flash.display.*;
import intrinsic.flash.net.*;
import intrinsic.flash.events.*;
import intrinsic.flash.geom.Rectangle;

import org.eclipse.swt.internal.image.*;

public final class Image extends Resource implements Drawable {

	public int type;
	
	int transparentPixel = -1;
	
	byte[] alphaData;
	
	int alpha = -1;
	
	GC memGC;
	
	public BitmapData object;
	public EventDispatcher listener;
	
	static WinICOFileFormat format1;
	static GIFFileFormat format2;
	static WinBMPFileFormat format3;
	static TIFFFileFormat format4;
	static JPEGFileFormat format5;
	static PNGFileFormat format6;
	static OS2BMPFileFormat format7;
	
Image (Device device) {
	super(device);
}
	
public Image (Device device, int width, int height) {
	super(device);
	init(width, height);
	init();
}

public Image (Device device, Image srcImage, int flag) {
	super(device);
	//TODO
	init();
}

public Image (Device device, org.eclipse.swt.graphics.Rectangle bounds) {
	super(device);
	if (bounds == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	init(bounds.width, bounds.height);
	init();
}

public Image (Device device, ImageData data) {
	super(device);
	init(data);
	init();
}

public Image (Device device, ImageData source, ImageData mask) {
	super(device);
	if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	if (mask == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	if (source.width != mask.width || source.height != mask.height) {
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	}
	mask = ImageData.convertMask (mask);
	ImageData image = new ImageData(source.width, source.height, source.depth, source.palette, source.scanlinePad, source.data);
	image.maskPad = mask.scanlinePad;
	image.maskData = mask.data;
	init(image);
	init();
}

public Image (Device device, InputStream stream) {
	super(device);
	init(new ImageData(stream));
	init();
}

public Image (Device device, String filename) {
	super(device);
	if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	if (true) {
		init(filename);
	} else {
		init(new ImageData(filename));
	}
	init();
}

void destroy () {
	object = null;
	listener = null;
}

public boolean equals (Object object) {
	if (object == this) return true;
	if (!(object instanceof Font)) return false;
	Image image = (Image) object;
	return device == image.device && object == image.object;
}

native ByteArray getArray(byte[] array)/*{
	return ByteArray(array.array);
}*/;

public Color getBackground () {
	if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
	return null;
}

public org.eclipse.swt.graphics.Rectangle getBounds () {
	if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
	return new org.eclipse.swt.graphics.Rectangle(0, 0, object.width, object.height);
}

public ImageData getImageData () {
	if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);

	int width = object.width;
	int height = object.height;
	int bpr = width * 4;
	Rectangle rect = new Rectangle();
	rect.width = width;
	rect.height = height;
	ByteArray array = object.getPixels(rect);
	byte[] srcData = newArray(array);
	
	
	PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF);
	ImageData data = new ImageData(width, height, 32, palette);
	data.data = srcData;
	data.bytesPerLine = bpr;

	data.transparentPixel = transparentPixel;
	if (transparentPixel == -1 && type == SWT.ICON) {
		/* Get the icon mask data */
		int maskPad = 2;
		int maskBpl = (((width + 7) / 8) + (maskPad - 1)) / maskPad * maskPad;
		byte[] maskData = new byte[height * maskBpl];
		int offset = 0, maskOffset = 0;
		for (int y = 0; y<height; y++) {
			for (int x = 0; x<width; x++) {
				if (srcData[offset] != 0) {
					maskData[maskOffset + (x >> 3)] |= (1 << (7 - (x & 0x7)));
				} else {
					maskData[maskOffset + (x >> 3)] &= ~(1 << (7 - (x & 0x7)));
				}
				offset += 4;
			}
			maskOffset += maskBpl;
		}
		data.maskData = maskData;
		data.maskPad = maskPad;
	}
	for (int i = 0; i < srcData.length; i+= 4) {
		srcData[i] = 0;
	}
	data.alpha = alpha;
	if (alpha == -1 && alphaData != null) {
		data.alphaData = new byte[alphaData.length];
		System.arraycopy(alphaData, 0, data.alphaData, 0, alphaData.length);
	}
	return data;
}

public int hashCode () {
	if (isDisposed()) return 0;
	return super.hashCode();
}

public int internal_new_GC (GCData data) {
	if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
	if (type != SWT.BITMAP || memGC != null) {
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	}
	if (data != null) {
		data.sprite = new Sprite();
		int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
		if ((data.style & mask) == 0) {
			data.style |= SWT.LEFT_TO_RIGHT;
		}
		data.device = device;
		data.background = device.getSystemColor(SWT.COLOR_WHITE);
		data.foreground = device.getSystemColor(SWT.COLOR_BLACK);
		data.font = device.systemFont;
		data.image = this;
		return 1;
	}
	return 0;
}

public void internal_dispose_GC (int hDC, GCData data) {
	if (data == null) return;
	object.draw(data.sprite);
}

void init(String filename) {
	String file = filename.toLowerCase();
	if (file.endsWith(".bmp") ||
		file.endsWith(".ico") ||
		file.endsWith(".tiff") ||
		file.endsWith(".tif"))
	{
		URLStream stream = new URLStream();
		stream.load(new URLRequest(filename));
		listener = stream;
	} else {
		Loader loader = new Loader();
		LoaderContext context = new LoaderContext();
		context.checkPolicyFile = true;
		loader.load(new URLRequest(filename), context);
		listener = loader.contentLoaderInfo;
	}
	listener.addEventListener(Event.COMPLETE, completeHandlerFunction ());
	listener.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandlerFunction ());
}

public org.eclipse.swt.widgets.Listener l;
void completeHandler (Event event) {
	if (event.target instanceof URLStream) {
		ByteArray b = new ByteArray();
		URLStream stream = (URLStream)event.target;
		stream.readBytes(b, 0, stream.bytesAvailable);
		init(new ImageData(new ByteArrayInputStream(newArray(b))));
	} else {
		object = ((Bitmap)((LoaderInfo)event.target).content).bitmapData;
	}
	if (l != null) l.handleEvent(null);
}

native Function completeHandlerFunction ()/*{
	return completeHandler__Lflash_events_Event_2;
}*/;

void ioErrorHandler(intrinsic.flash.events.IOErrorEvent event) {
	System.out.println(event.text);
}

native Function ioErrorHandlerFunction ()/*{
	return ioErrorHandler__Lflash_events_IOErrorEvent_2;
}*/;

native byte[] newArray(ByteArray b)/*{
	var array:JavaArray = new JavaArray("[B");
	array.array = b;
	return array;
}*/;

void init(int width, int height) {
	if (width <= 0 || height <= 0) {
		SWT.error (SWT.ERROR_INVALID_ARGUMENT);
	}
	this.type = SWT.BITMAP;
	object = new BitmapData(width, height, false, 0xFFFFFFFF);
	if (object == null) SWT.error(SWT.ERROR_NO_HANDLES);
}

void init(ImageData image) {
	if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	PaletteData palette = image.palette;
	if (!(((image.depth == 1 || image.depth == 2 || image.depth == 4 || image.depth == 8) && !palette.isDirect) ||
		((image.depth == 8) || (image.depth == 16 || image.depth == 24 || image.depth == 32) && palette.isDirect)))
			SWT.error (SWT.ERROR_UNSUPPORTED_DEPTH);
	int width = image.width;
	int height = image.height;
	
	byte[] buffer = new byte[width * 4 * height];
	if (palette.isDirect) {
		ImageData.blit(ImageData.BLIT_SRC,
			image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, palette.redMask, palette.greenMask, palette.blueMask,
			ImageData.ALPHA_OPAQUE, null, 0, 0, 0, 
			buffer, 32, width * 4, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF,
			false, false);
	} else {
		RGB[] rgbs = palette.getRGBs();
		int length = rgbs.length;
		byte[] srcReds = new byte[length];
		byte[] srcGreens = new byte[length];
		byte[] srcBlues = new byte[length];
		for (int i = 0; i < rgbs.length; i++) {
			RGB rgb = rgbs[i];
			if (rgb == null) continue;
			srcReds[i] = (byte)rgb.red;
			srcGreens[i] = (byte)rgb.green;
			srcBlues[i] = (byte)rgb.blue;
		}
		ImageData.blit(ImageData.BLIT_SRC,
			image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, srcReds, srcGreens, srcBlues,
			ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
			buffer, 32, width * 4, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF,
			false, false);
	}
	
	/* Initialize transparency */
	int transparency = image.getTransparencyType(); 
	if (transparency == SWT.TRANSPARENCY_MASK || image.transparentPixel != -1) {
		this.type = image.transparentPixel != -1 ? SWT.BITMAP : SWT.ICON;
		if (image.transparentPixel != -1) {
			int transRed = 0, transGreen = 0, transBlue = 0;
			if (palette.isDirect) {
				RGB rgb = palette.getRGB(image.transparentPixel);
				transRed = rgb.red;
				transGreen = rgb.green;
				transBlue = rgb.blue;
			} else {
				RGB[] rgbs = palette.getRGBs();
				if (image.transparentPixel < rgbs.length) {
					RGB rgb = rgbs[image.transparentPixel];
					transRed = rgb.red;
					transGreen = rgb.green;
					transBlue = rgb.blue;				
				}
			}
			transparentPixel = transRed << 16 | transGreen << 8 | transBlue;
		}
		ImageData maskImage = image.getTransparencyMask();
		byte[] maskData = maskImage.data;
		int maskBpl = maskImage.bytesPerLine;
		int offset = 0, maskOffset = 0;
		for (int y = 0; y<height; y++) {
			for (int x = 0; x<width; x++) {
				buffer[offset] = ((maskData[maskOffset + (x >> 3)]) & (1 << (7 - (x & 0x7)))) != 0 ? (byte)0xff : 0;
				offset += 4;
			}
			maskOffset += maskBpl;
		}
	} else {
		this.type = SWT.BITMAP;
		if (image.alpha != -1) {
			this.alpha = image.alpha;
			byte a = (byte)this.alpha;
			for (int dataIndex=0; dataIndex<buffer.length; dataIndex+=4) {
				buffer[dataIndex] = a;				
			}
		} else if (image.alphaData != null) {
			this.alphaData = new byte[image.alphaData.length];
			System.arraycopy(image.alphaData, 0, this.alphaData, 0, alphaData.length);
			int offset = 0, alphaOffset = 0;
			for (int y = 0; y<height; y++) {
				for (int x = 0; x<width; x++) {
					buffer[offset] = alphaData[alphaOffset];
					offset += 4;
					alphaOffset += 1;
				}
			}
		} else {
			for (int dataIndex=0; dataIndex<buffer.length; dataIndex+=4) {
				buffer[dataIndex] = (byte)0xff;				
			}
		}		
	}

	object = new BitmapData(width, height, true, 0xFFFFFFFF);
	if (object == null) SWT.error(SWT.ERROR_NO_HANDLES);
	intrinsic.flash.geom.Rectangle rect = new intrinsic.flash.geom.Rectangle(0, 0, width, height);
	object.setPixels(rect, getArray(buffer));
}

public boolean isDisposed () {
	return device == null;
}

public void setBackground (Color color) {
	if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
}

public String toString () {
	if (isDisposed()) return "Image {*DISPOSED*}";
	return "Image {" + 1 + "}";
}

public static Image flex_new (Device device, BitmapData object) {
	Image image = new Image(device);
	image.object = object;
	return image;
}

}
