package tk.fusselrulezz.ets2editor.util;

import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Transparency;
import java.awt.image.BufferedImage;

/**
 * Provides graphic utilities
 * 
 * @author fusselrulezz
 */
public class GraphicsUtils {
	
	private static GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
	
	/**
	 * Transforms a {@link BufferedImage} to a compatible BufferedImage.
	 * See {@link GraphicsConfiguration#createCompatibleImage(int, int, int)} for more information.
	 * 
	 * @param image Image to convert into a compatible image
	 * @return A new compatible image
	 */
	public static BufferedImage toCompatibleImage(BufferedImage image) {
		if(image.getColorModel().equals(config.getColorModel())) {
			return image;
		} else {
			BufferedImage compatible = config.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
			
			Graphics2D graphics = (Graphics2D) compatible.getGraphics();
			graphics.drawImage(image, 0, 0, null);
			graphics.dispose();
			
			return compatible;
		}
	}
	
	/**
	 * Creates a new hardware accelerated, compatible buffered image using the default graphics configuration.
	 * 
	 * @param width Width of the image to create
	 * @param height Height of the image to create
	 * @param alpha Set this to <code>true</code> if the image is supposed to have an transparency (alpha) channel.
	 * @return A new hardware accelerated, compatible buffered image.
	 */
	public static BufferedImage createCompatibleImage(int width, int height, boolean alpha) {
		return config.createCompatibleImage(width, height, alpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE);
	}
	
	/**
	 * Converts a simple {@link Image} object to an {@link BufferedImage}.
	 * 
	 * @param img Image to convert
	 * @return The converted image.
	 */
	public static BufferedImage toBufferedImage(Image img) {
		if(img instanceof BufferedImage) {
			return (BufferedImage) img;
		}
		
		BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
		
		Graphics2D bGr = bimage.createGraphics();
		bGr.drawImage(img, 0, 0, null);
		bGr.dispose();
		
		return bimage;
	}
	
	/**
	 * Creates a scaled pixel array of the given pixel array using the "Nearest Neighbor" algorithm.
	 * 
	 * @param pixels Image's pixels
	 * @param w1 Width of the image
	 * @param h1 Height of the image
	 * @param w2 Destination image width
	 * @param h2 Destination image height
	 * @return Returns a scaled pixel array.
	 */
	public static int[] resizePixels(int[] pixels, int w1, int h1, int w2, int h2) {
		int[] temp = new int[w2 * h2];
		int x_ratio = (int) ((w1 << 16) / w2) + 1;
		int y_ratio = (int) ((h1 << 16) / h2) + 1;
		int x2, y2;
		
		for(int i = 0; i < h2; i++) {
			for(int j = 0; j < w2; j++) {
				x2 = ((j * x_ratio) >> 16);
				y2 = ((i * y_ratio) >> 16);
				temp[(i * w2) + j] = pixels[(y2 * w1) + x2];
			}                
		}                
		return temp;
	}
	
	/**
	 * Sets the brightness for each individual pixel in an pixel array.
	 * 
	 * @param pixels Pixel array to set the brightness to
	 * @param factor Brightness factor, positive for increasing and negative for decreasing the brightness
	 * @return The modified pixel array
	 */
	public static int[] setBrightness(int[] pixels, int factor) {
		int[] changed = new int[pixels.length];
		
		for(int i = 0; i < pixels.length; i++) {
			int r = clamp(((pixels[i] >> 16) & 0xFF) + factor, 0, 255);
			int g = clamp(((pixels[i] >>  8) & 0xFF) + factor, 0, 255);
			int b = clamp(((pixels[i] >>  0) & 0xFF) + factor, 0, 255);
			
			changed[i] = (r << 16) | (g << 8) | b;
		}
		
		return changed;
	}
	
	/**
	 * Sets the brightness for a pixel
	 * 
	 * @param pixel Pixel
	 * @param factor factor Brightness factor, positive for increasing and negative for decreasing the brightness
	 * @return The modified pixel
	 */
	public static int setBrightness(int pixel, int factor) {
		int r = clamp(((pixel >> 16) & 0xFF) + factor, 0, 255);
		int g = clamp(((pixel >>  8) & 0xFF) + factor, 0, 255);
		int b = clamp(((pixel >>  0) & 0xFF) + factor, 0, 255);
		return (r << 16) | (g << 8) | b;
	}
	
	private static int clamp(int value, int min, int max) {
		if(value < min) return min;
		if(value >= max) return max;
		return value;
	}
}