package speiger.src.coreengine.math.color;

import speiger.src.coreengine.math.MathUtils;

public enum ColorSpaces {
	ARGB {
		@Override
		public int color(int r, int g, int b, int a) { return (a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF); }
		@Override
		public int r(int color) { return color >> 16 & 0xFF; }
		@Override
		public int g(int color) { return color >> 8 & 0xFF; }
		@Override
		public int b(int color) { return color & 0xFF; }
		@Override
		public int a(int color) { return color >> 24; }
		@Override
		public int setR(int color, int r) { return color & ~ColorUtils.R | ((r & 0xFF) << 16); }
		@Override
		public int setG(int color, int g) { return color & ~ColorUtils.G | ((g & 0xFF) << 8); }
		@Override
		public int setB(int color, int b) { return color & ~ColorUtils.B | (b & 0xFF); }
		@Override
		public int setA(int color, int a) { return color & ~ColorUtils.A | ((a & 0xFF) << 24); }
		@Override
		public int fromARGB(int color) { return color; }
		@Override
		public int toARGB(int color) { return color; }
	},
	ABGR {
		@Override
		public int color(int r, int g, int b, int a) { return (a & 0xFF) << 24 | (b & 0xFF) << 16 | (g & 0xFF) << 8 | (r & 0xFF); }
		@Override
		public int r(int color) { return color & 0xFF; }
		@Override
		public int g(int color) { return color >> 8 & 0xFF; }
		@Override
		public int b(int color) { return color >> 16 & 0xFF; }
		@Override
		public int a(int color) { return color >> 24 & 0xFF; }
		@Override
		public int setR(int color, int r) { return color & ~ColorUtils.B | (r & 0xFF); }
		@Override
		public int setG(int color, int g) { return color & ~ColorUtils.G | ((g & 0xFF) << 8); }
		@Override
		public int setB(int color, int b) { return color & ~ColorUtils.R | ((b & 0xFF) << 16); }
		@Override
		public int setA(int color, int a) { return color & ~ColorUtils.A | ((a & 0xFF) << 24); }
		@Override
		public int fromARGB(int color) { return color & 0xFF00FF00 | (color & 0xFF0000) >> 16 | (color & 0xFF) << 16; }
		@Override
		public int toARGB(int color) { return color & 0xFF00FF00 | (color & 0xFF0000) >> 16 | (color & 0xFF) << 16; }
	};
	
	public int color(float r, float g, float b) { return color((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F)); }
	public int color(float r, float g, float b, float a) { return color((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F), (int)(a * 255F + 0.5F));}
	public int color(int r, int g, int b) { return color(r, g, b, 255); }
	public abstract int color(int r, int g, int b, int a);
	public abstract int r(int color);
	public abstract int g(int color);
	public abstract int b(int color);
	public abstract int a(int color);
	public float rf(int color) { return r(color) / 255F; }
	public float gf(int color) { return g(color) / 255F; }
	public float bf(int color) { return b(color) / 255F; }
	public float af(int color) { return a(color) / 255F; }
	public abstract int setR(int color, int r);
	public abstract int setG(int color, int g);
	public abstract int setB(int color, int b);
	public abstract int setA(int color, int a);
	public int setRF(int color, float r) { return setR(color, (int)(r * 255F + 0.5F)); }
	public int setGF(int color, float g) { return setG(color, (int)(g * 255F + 0.5F)); }
	public int setBF(int color, float b) { return setB(color, (int)(b * 255F + 0.5F)); }
	public int setAF(int color, float a) { return setA(color, (int)(a * 255F + 0.5F)); }
	public abstract int toARGB(int color);
	public abstract int fromARGB(int color);
	
	public int darker(int color) { return darker(color, 0.7F); }
	public int darker(int color, float factor) {
		int r = Math.max(0, (int)(r(color) * factor));
		int g = Math.max(0, (int)(g(color) * factor));
		int b = Math.max(0, (int)(b(color) * factor));
		return (a(color) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
	}
	
	public int brighter(int color) { return brighter(color, 0.7F); }
	public int brighter(int color, float factor) {
		int r = r(color);
		int g = g(color);
		int b = b(color);
		int i = (int)(1.0 / (1.0 - factor));
		if(r == 0 && g == 0 && b == 0) { return color(i & 0xFF, i & 0xFF, i & 0xFF, a(color)); }
		if(r > 0 && r < i) r = i;
		if(g > 0 && g < i) g = i;
		if(b > 0 && b < i) b = i;
		return color(Math.min(255, (int)(r / factor)), Math.min(255, (int)(g / factor)), Math.min(255, (int)(b / factor)), a(color) << 24);
	}
	
	public int fromHue(float hue, float saturation, float brightness) {
		if(saturation == 0) {
			int result = (int)(brightness * 255F + 0.5F);
			return color(result, result, result);
		}
		float h = (hue - MathUtils.floor(hue)) * 6F;
		float f = h - MathUtils.floor(h);
		float p = brightness * (1F - saturation);
		float q = brightness * (1F - saturation * f);
		float t = brightness * (1F - (saturation * (1F - f)));
		switch((int)h) {
			case 0: return color(brightness, t, p);
			case 1: return color(q, brightness, p);
			case 2: return color(p, brightness, t);
			case 3: return color(p, q, brightness);
			case 4: return color(t, p, brightness);
			case 5: return color(brightness, p, q);
			default: return color(0, 0, 0);
		}
	}
	
	public float[] toHue(int color) {
		int r = r(color);
		int g = g(color);
		int b = b(color);
		int cmax = (r > g) ? r : g;
		if(b > cmax) cmax = b;
		int cmin = (r < g) ? r : g;
		if(b < cmin) cmin = b;
		float length = cmax - cmin;
		
		float[] result = new float[3];
		result[1] = cmax == 0 ? 0F : length / cmax;
		result[2] = cmax * ColorUtils.DEVIDER;
		float hue = 0F;
		if(result[1] != 0F) {
			float redc = (cmax - r) / length;
			float greenc = (cmax - g) / length;
			float bluec = (cmax - b) / length;
			if(r == cmax) hue = bluec - greenc;
			else if(g == cmax) hue = 2F + redc - bluec;
			else hue = 4F + greenc - redc;
			hue /= 6F;
			if(hue < 0) hue += 1F;
		}
		result[0] = hue;
		return result;
	}
}
