/*******************************************************************************
 * 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 java.io.*;

import org.eclipse.swt.*;

public final class Image extends Resource implements Drawable {

	public int type;
	
	int width = -1, height = -1;
	
	int transparentPixel = -1;
	
	byte[] alphaData;
	
	int alpha = -1;
	
	public String imagePath;
	
	GC memGC;
	
	static final String base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	
Image () {
}
	
public Image (Device device, int width, int height) {
	if (device == null) device = Device.getDevice();
	if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	init(device, width, height);
	if (device.tracking) device.new_Object(this);
}

public Image (Device device, Image srcImage, int flag) {
}

public Image (Device device, Rectangle bounds) {
	if (device == null) device = Device.getDevice();
	if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	if (bounds == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	init(device, bounds.width, bounds.height);
	if (device.tracking) device.new_Object(this);
}

public Image (Device device, ImageData data) {
	if (device == null) device = Device.getDevice();
	if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	init(device, data);
	if (device.tracking) device.new_Object(this);
}

public Image (Device device, ImageData source, ImageData mask) {
	if (device == null) device = Device.getDevice();
	if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	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(device, image);
	if (device.tracking) device.new_Object(this);
}

public Image (Device device, InputStream stream) {
	if (device == null) device = Device.getDevice();
	if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	this.device = device;
	//init(device, new ImageData(stream));
	ByteArrayOutputStream out = new ByteArrayOutputStream();
	byte[] buffer = new byte[4096];
	int read;
	try {
		while ((read = stream.read(buffer)) != -1) {
			out.write(buffer,0, read);
		}
	} catch (IOException e) {
		throw new SWTException(SWT.ERROR_IO);
	}
	imagePath = "data:image/gif;base64," + encode(out.toByteArray());
	if (device.tracking) device.new_Object(this);
}

public Image (Device device, String filename) {
	if (device == null) device = Device.getDevice();
	if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	this.device = device;
	this.imagePath = filename;
	//init(device, new ImageData(filename));
	if (device.tracking) device.new_Object(this);
}

public Image (String imagePath) {
	if (imagePath == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	this.imagePath = imagePath;
}

public Image (Device device, String imagePath, int width, int height) {
	super(device);
	if (imagePath == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	this.device = device;
	this.imagePath = imagePath;
	this.width = width;
	this.height = height;
}

void destroy () {
	imagePath = null;
}

static String encode(byte[] buffer){
	StringBuffer resultBuffer = new StringBuffer();
	int count = 2;
	for (int i=0; i<buffer.length; i+=3) {
		int c = (buffer[i] & 0xFF) << 16;
		count = 2;
		if (i+1 < buffer.length) {
			count++;
			c |= (buffer[i+1]& 0xFF)  << 8;
		}
		if (i+2 < buffer.length) {
			count++;
			c |= buffer[i+2] & 0xFF;
		}
		
		resultBuffer.append(base64Table.charAt((c >> 18) & 0x3F));
		resultBuffer.append(base64Table.charAt((c >> 12) & 0x3F));
		if (count >= 3) {
			resultBuffer.append(base64Table.charAt((c >> 6) & 0x3F));
		}
		if (count == 4) {
			resultBuffer.append(base64Table.charAt((c >> 0) & 0x3F));
		}
		
	}
	if (count==3) {
		resultBuffer.append('=');	
	} else if (count ==2) {
		resultBuffer.append('=');
		resultBuffer.append('=');	
	} 
	
	return resultBuffer.toString();
}

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

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

public Rectangle getBounds () {
	if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
	if (width != -1 && height != -1) {
		return new Rectangle(0, 0, width, height);
	}
	return new Rectangle(0, 0, 0, 0);
}

public ImageData getImageData () {
	return null;
}

public int hashCode () {
	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) {
		//TODO uncomment if necessary
		//getServer().callNew(data.gc, new Object[]{this});
		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) {
//	data.gc.callMethod("dispose", null, null);
}

void init(Device device, int width, int height) {
	if (width <= 0 || height <= 0) {
		SWT.error (SWT.ERROR_INVALID_ARGUMENT);
	}
	this.device = device;
	this.type = SWT.BITMAP;
	this.width = width;
	this.height = height;
//	callNew( new Object[]{new Integer(width), new Integer(height), null});
}

void init(Device device, ImageData image) {
	if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	this.device = device;
	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);
	this.width = image.width;
	this.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;				
			}
		}		
	}
	
//	callNew( new Object[]{new Integer(width), new Integer(height), 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 {" + 0 + "}";
}

}
