package speiger.src.coreengine.math.color;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import speiger.src.collections.floats.lists.FloatList;

public class ColorUtils {
	static final float DEVIDER = 1F / 255F;
	public static final int R = 0xFF << 16;
	public static final int G = 0xFF << 8;
	public static final int B = 0xFF;
	public static final int A = 0xFF << 24;
	public static final long SIGN = 0x00000000FFFFFFFFL;
	static final int ALL = 0xFFFFFFFF;
	public static final int WHITE = rgb(255, 255, 255);
	public static final int LIGHT_GRAY = rgb(192, 192, 192);
	public static final int GRAY = rgb(128, 128, 128);
	public static final int DARK_GRAY = rgb(64, 64, 64);
	public static final int BLACK = rgb(0, 0, 0);
	public static final int RED = rgb(255, 0, 0);
	public static final int PINK = rgb(255, 175, 175);
	public static final int PURPLE = rgb(106, 13, 173);
	public static final int ORANGE = rgb(255, 200, 0);
	public static final int YELLOW = rgb(255, 255, 0);
	public static final int GREEN = rgb(0, 255, 0);
	public static final int DARK_GREEN = rgb(7, 161, 0);
	public static final int MAGENTA = rgb(255, 0, 255);
	public static final int CYAN = rgb(0, 255, 255);
	public static final int BLUE = rgb(0, 0, 255);
	public static final int LIGHT_BLUE = rgb(0, 150, 255);
	
	// Specialized Components that get reused
	public static final int INVISIBLE = rgb(0, 0, 0, 0);
	public static final int TEXT_DEFAULT_BACKGROUND = rgb(80, 80, 80, 144);
	public static final int WINDOW_DEFAULT_BACKGROUND = rgb(64, 64, 64, 128);
	public static final int POPUP_DEFAULT_BACKGROUND = rgb(85, 85, 85);
	public static final int DESTRUCTION = rgb(255, 0, 0, 128);
	
	public static byte[] toByteArray(int color, boolean alpha) {
		byte[] data = new byte[alpha ? 4 : 3];
		data[0] = (byte)((color >> 16) & 0xFF);
		data[1] = (byte)((color >> 8) & 0xFF);
		data[2] = (byte)(color & 0xFF);
		if(alpha) data[3] = (byte)((color >> 24) & 0xFF);
		return data;
	}
	
	public static void write(int color, boolean alpha, ByteBuffer buffer) {
		buffer.put((byte)((color >> 16) & 0xFF)).put((byte)((color >> 8) & 0xFF)).put((byte)(color & 0xFF));
		if(alpha) buffer.put((byte)((color >> 24) & 0xFF));
	}
	
	public static void write(int index, int color, boolean alpha, ByteBuffer buffer) {
		buffer.put(index, (byte)((color >> 16) & 0xFF)).put(index + 1, (byte)((color >> 8) & 0xFF)).put(index + 2, (byte)(color & 0xFF));
		if(alpha) buffer.put(index + 3, (byte)((color >> 24) & 0xFF));
	}
	
	public static void write(int color, boolean alpha, FloatBuffer buffer) {
		buffer.put(((color >> 16) & 0xFF) * DEVIDER).put(((color >> 8) & 0xFF) * DEVIDER).put((color & 0xFF) * DEVIDER);
		if(alpha) buffer.put(((color >> 24) & 0xFF) * DEVIDER);
	}
	
	public static void write(int index, int color, boolean alpha, FloatBuffer buffer) {
		buffer.put(index, ((color >> 16) & 0xFF) * DEVIDER).put(index + 1, ((color >> 8) & 0xFF) * DEVIDER).put(index + 2, (color & 0xFF) * DEVIDER);
		if(alpha) buffer.put(index + 3, ((color >> 24) & 0xFF) * DEVIDER);
	}
	
	public static void write(int color, boolean alpha, FloatList list) {
		list.add(((color >> 16) & 0xFF) * DEVIDER);
		list.add(((color >> 8) & 0xFF) * DEVIDER);
		list.add((color & 0xFF) * DEVIDER);
		if(alpha) list.add(((color >> 24) & 0xFF) * DEVIDER);
	}
	
	public static int read(ByteBuffer buffer, boolean alpha) { return alpha ? rgb(buffer.get(), buffer.get(), buffer.get()) : rgb(buffer.get(), buffer.get(), buffer.get(), buffer.get()); }
	public static int read(ByteBuffer buffer, int index, boolean alpha) { return alpha ? rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2)) : rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2), buffer.get(index + 3)); }
	public static int read(FloatBuffer buffer, boolean alpha) { return alpha ? rgb(buffer.get(), buffer.get(), buffer.get()) : rgb(buffer.get(), buffer.get(), buffer.get(), buffer.get()); }
	public static int read(FloatBuffer buffer, int index, boolean alpha) { return alpha ? rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2)) : rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2), buffer.get(index + 3)); }
	public static boolean needsDarkColor(int rgba) { return getBrightness(rgba) >= 130; }
	public static int getBrightness(int rgba) { return getBrightness((rgba >> 16) & 0xFF, (rgba >> 8) & 0xFF, rgba & 0xFF); }
	public static int getBrightness(int r, int g, int b) { return (int)Math.sqrt((r * r * 0.241F) + (g * g * 0.691F) + (b * b * 0.068F)); }
	
	public static int mix(int from, int to, float factor) {
		float weight0 = (1F - factor);
		float weight1 = factor;
		int r = (int)((((from >> 16) & 0xFF) * weight0) + (((to >> 16) & 0xFF) * weight1));
		int g = (int)((((from >> 8) & 0xFF) * weight0) + (((to >> 8) & 0xFF) * weight1));
		int b = (int)(((from & 0xFF) * weight0) + ((to & 0xFF) * weight1));
		int a = (int)((((from >> 24) & 0xFF) * weight0) + (((to >> 24) & 0xFF) * weight1));
		return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | b & 0xFF;
	}
	
	public static int rgb(int rgb) { return rgb | (255 << 24); }
	public static int rgb(int r, int g, int b) { return A | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); }
	public static int rgb(float r, float g, float b) { return rgb((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F)); }
	public static int rgb(int r, int g, int b, int a) { return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); }
	public static int rgb(float r, float g, float b, float a) { return rgb((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F), (int)(b * 255F + 0.5F)); }
	public static int setR(int rgba, int r) { return rgba & ~R | ((r & 0xFF) << 16); }
	public static int setR(int rgba, float r) { return rgba & ~R | (((int)(r * 255F + 0.5F)) << 16); }
	public static int setG(int rgba, int g) { return rgba & ~G | ((g & 0xFF) << 8); }
	public static int setG(int rgba, float g) { return rgba & ~G | (((int)(g * 255F + 0.5F)) << 8); }
	public static int setB(int rgba, int b) { return rgba & ~B | (b & 0xFF); }
	public static int setB(int rgba, float b) { return rgba & ~B | ((int)(b * 255F + 0.5F)); }
	public static int setA(int rgba, int a) { return rgba & ~A | ((a & 0xFF) << 24); }
	public static int setA(int rgba, float a) { return rgba & ~A | (((int)(a * 255F + 0.5F)) << 24); }
	public static int getR(int rgba) { return (rgba >> 16) & 0xFF; }
	public static float getRF(int rgba) { return ((rgba >> 16) & 0xFF) * DEVIDER; }
	public static int getG(int rgba) { return (rgba >> 8) & 0xFF; }
	public static float getGF(int rgba) { return ((rgba >> 8) & 0xFF) * DEVIDER; }
	public static int getB(int rgba) { return rgba & 0xFF; }
	public static float getBF(int rgba) { return (rgba & 0xFF) * DEVIDER; }
	public static int getA(int rgba) { return (rgba >> 24) & 0xFF; }
	public static float getAF(int rgba) { return ((rgba >> 24) & 0xFF) * DEVIDER; }
	public static int parse(String input) { 
		try { return Long.decode(input).intValue(); }
		catch (Exception e) { return -1; }
	}
	public static int parse(String input, int defaultValue) { 
		try { return Long.decode(input).intValue(); }
		catch (Exception e) { return defaultValue; }
	}
	public static String toHex(int rgba, boolean alpha) { return "0x"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); }
	public static String toHTML(int rgba, boolean alpha) { return "#"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); }
}
