package com.wudsn.ide.gfx.converter.atari8bit;

import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;

// TODO Class Atari8BitPaletteUtility2 is currently not used
public class Atari8BitPaletteUtility2 {

    private final int PAL_ENTRIES_NO = 256;
    private final int BAR_LINES_NO = 16;
    private final int BAR_ENTRIES_NO = (PAL_ENTRIES_NO / BAR_LINES_NO);

    static class RGB {
	int R, G, B;

	public static RGB FromArgb(int r1, int g1, int b1) {
	    RGB color = new RGB();
	    color.R = r1;
	    color.G = g1;
	    color.B = b1;
	    return color;
	}
    }

    private RGB[] actual_pal = new RGB[PAL_ENTRIES_NO];

    class LAB {
	public double L;
	public double a;
	public double b;
    }

    private LAB[] cielab = new LAB[PAL_ENTRIES_NO];

    private int min_y = 0, max_y = 0xf0;
    private int colintens = 100;
    private int colshift = 30;

    // public int BlackLevel
    // {
    // get { return min_y; }
    // set { min_y = value; CalcPaletteFromSettings(); Highlight =
    // highlightColor; }
    // }
    //
    // public int WhiteLevel
    // {
    // get { return max_y; }
    // set { max_y = value; CalcPaletteFromSettings(); Highlight =
    // highlightColor; }
    // }
    //
    // public int Saturation
    // {
    // get { return colintens; }
    // set { colintens = value; CalcPaletteFromSettings(); Highlight =
    // highlightColor; }
    // }
    //
    // public int ColorShift
    // {
    // get { return colshift; }
    // set { colshift = value; CalcPaletteFromSettings(); Highlight =
    // highlightColor; }
    // }

    private static int[] real_pal = { 0x323132, 0x3f3e3f, 0x4d4c4d, 0x5b5b5b, 0x6a696a, 0x797879, 0x888788, 0x979797,
	    0xa1a0a1, 0xafafaf, 0xbebebe, 0xcecdce, 0xdbdbdb, 0xebeaeb, 0xfafafa, 0xffffff, 0x612e00, 0x6c3b00,
	    0x7a4a00, 0x885800, 0x94670c, 0xa5761b, 0xb2842a, 0xc1943a, 0xca9d43, 0xdaad53, 0xe8bb62, 0xf8cb72,
	    0xffd87f, 0xffe88f, 0xfff79f, 0xffffae, 0x6c2400, 0x773000, 0x844003, 0x924e11, 0x9e5d22, 0xaf6c31,
	    0xbc7b41, 0xcc8a50, 0xd5935b, 0xe4a369, 0xf2b179, 0xffc289, 0xffcf97, 0xffdfa6, 0xffedb5, 0xfffdc4,
	    0x751618, 0x812324, 0x8f3134, 0x9d4043, 0xaa4e50, 0xb85e60, 0xc66d6f, 0xd57d7f, 0xde8787, 0xed9596,
	    0xfca4a5, 0xffb4b5, 0xffc2c4, 0xffd1d3, 0xffe0e1, 0xffeff0, 0x620e71, 0x6e1b7c, 0x7b2a8a, 0x8a3998,
	    0x9647a5, 0xa557b5, 0xb365c3, 0xc375d1, 0xcd7eda, 0xdc8de9, 0xea97f7, 0xf9acff, 0xffbaff, 0xffc9ff,
	    0xffd9ff, 0xffe8ff, 0x560f87, 0x611d90, 0x712c9e, 0x7f3aac, 0x8d48ba, 0x9b58c7, 0xa967d5, 0xb877e5,
	    0xc280ed, 0xd090fc, 0xdf9fff, 0xeeafff, 0xfcbdff, 0xffccff, 0xffdbff, 0xffeaff, 0x461695, 0x5122a0,
	    0x6032ac, 0x6e41bb, 0x7c4fc8, 0x8a5ed6, 0x996de3, 0xa87cf2, 0xb185fb, 0xc095ff, 0xcfa3ff, 0xdfb3ff,
	    0xeec1ff, 0xfcd0ff, 0xffdfff, 0xffefff, 0x212994, 0x2d359f, 0x3d44ad, 0x4b53ba, 0x5961c7, 0x686fd5,
	    0x777ee2, 0x878ef2, 0x9097fa, 0x96a6ff, 0xaeb5ff, 0xbfc4ff, 0xcdd2ff, 0xdae3ff, 0xeaf1ff, 0xfafeff,
	    0x0f3584, 0x1c418d, 0x2c509b, 0x3a5eaa, 0x486cb7, 0x587bc5, 0x678ad2, 0x7699e2, 0x80a2eb, 0x8fb2f9,
	    0x9ec0ff, 0xadd0ff, 0xbdddff, 0xcbecff, 0xdbfcff, 0xeaffff, 0x043f70, 0x114b79, 0x215988, 0x2f6896,
	    0x3e75a4, 0x4d83b2, 0x5c92c1, 0x6ca1d2, 0x74abd9, 0x83bae7, 0x93c9f6, 0xa2d8ff, 0xb1e6ff, 0xc0f5ff,
	    0xd0ffff, 0xdeffff, 0x005918, 0x006526, 0x0f7235, 0x1d8144, 0x2c8e50, 0x3b9d60, 0x4aac6f, 0x59bb7e,
	    0x63c487, 0x72d396, 0x82e2a5, 0x92f1b5, 0x9ffec3, 0xaeffd2, 0xbeffe2, 0xcefff1, 0x075c00, 0x146800,
	    0x227500, 0x328300, 0x3f910b, 0x4fa01b, 0x5eae2a, 0x6ebd3b, 0x77c644, 0x87d553, 0x96e363, 0xa7f373,
	    0xb3fe80, 0xc3ff8f, 0xd3ffa0, 0xe3ffb0, 0x1a5600, 0x286200, 0x367000, 0x457e00, 0x538c00, 0x629b07,
	    0x70a916, 0x80b926, 0x89c22f, 0x99d13e, 0xa8df4d, 0xb7ef5c, 0xc5fc6b, 0xd5ff7b, 0xe3ff8b, 0xf3ff99,
	    0x334b00, 0x405700, 0x4d6500, 0x5d7300, 0x6a8200, 0x7a9100, 0x889e0f, 0x98ae1f, 0xa1b728, 0xbac638,
	    0xbfd548, 0xcee458, 0xdcf266, 0xebff75, 0xfaff85, 0xffff95, 0x4b3c00, 0x584900, 0x655700, 0x746500,
	    0x817400, 0x908307, 0x9f9116, 0xaea126, 0xb7aa2e, 0xc7ba3e, 0xd5c74d, 0xe5d75d, 0xf2e56b, 0xfef47a,
	    0xffff8b, 0xffff9a, 0x602e00, 0x6d3a00, 0x7a4900, 0x895800, 0x95670a, 0xa4761b, 0xb2832a, 0xc2943a,
	    0xcb9d44, 0xdaac53, 0xe8ba62, 0xf8cb73, 0xffd77f, 0xffe791, 0xfff69f, 0xffffaf, };

    private int highlightIndex = -1;

    // public short A8Hue
    // {
    // get { return (short)(highlightIndex >> 4); }
    // }
    //
    // public short A8Saturation
    // {
    // get { return (short)(highlightIndex & 15); }
    // }

    public int CalcHighlightLAB(RGB c) {
	double[] calc = new double[PAL_ENTRIES_NO];
	double l2, a2, b2, m = 0xFFFFFF;
	int i, j = -1;
	LAB lab = new LAB();

	log("Looking for: R=" + c.R + ", G=" + c.G + ", B=" + c.B);
	RGB2LAB(c, lab);
	log("Looking for: L=" + lab.L + ", a=" + lab.a + ", b=" + lab.b);

	for (i = 0; i < PAL_ENTRIES_NO; i++) {
	    l2 = lab.L - cielab[i].L;
	    l2 = l2 * l2;
	    a2 = lab.a - cielab[i].a;
	    a2 = a2 * a2;
	    b2 = lab.b - cielab[i].b;
	    b2 = b2 * b2;
	    calc[i] = l2 + a2 + b2;
	}

	for (i = 0; i < PAL_ENTRIES_NO; i++) {
	    log("Distance to: L=" + cielab[i].L + ", a=" + cielab[i].a + ", b=" + cielab[i].b + " is " + calc[i]);
	    if (calc[i] < m) {
		m = calc[i];
		j = i;
		log(" : New minimum!");
	    } else if (calc[i] == m) {
		log(" : Equal min!");
	    } else {
		log("");
	    }
	}

	log("Closest Color: m=" + m + " (R=" + actual_pal[j].R + ", G=" + actual_pal[j].G + ", B=" + actual_pal[j].B
		+ ")");

	return j;
    }

    public int CalcHighlightRGB(RGB c) {
	long[] calc = new long[PAL_ENTRIES_NO];
	long r2, g2, b2, m = 0xFFFFFF, mg = 0xFFFFFF;
	int i, j = -1, k = -1;

	log("Looking for: R=" + c.R + ", G=" + c.G + ", B=" + c.B);
	for (i = 0; i < PAL_ENTRIES_NO; i++) {
	    // (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b)
	    r2 = c.R - actual_pal[i].R;
	    r2 = r2 * r2;
	    g2 = c.G - actual_pal[i].G;
	    g2 = g2 * g2;
	    b2 = c.B - actual_pal[i].B;
	    b2 = b2 * b2;
	    // calc[i] = (((512+rm)*r2)>>8) + 4*g2 + (((767-rm)*b2)>>8);
	    calc[i] = (2 * r2) + (4 * g2) + (3 * b2);
	}

	for (i = 0; i < 16; i++) {
	    log("Distance to: R=" + actual_pal[i].R + ", G=" + actual_pal[i].G + ", B=" + actual_pal[i].B + " is "
		    + calc[i]);
	    if (calc[i] < mg) {
		mg = calc[i];
		k = i;
		log(" : New minimum!");
	    } else if (calc[i] == mg) {
		log(" : Equal min!");
	    } else {
		log("");
	    }
	}

	for (i = 16; i < 256; i++) {
	    log("Distance to: R=" + actual_pal[i].R + ", G=" + actual_pal[i].G + ", B=" + actual_pal[i].B + " is "
		    + calc[i]);
	    if (calc[i] < m) {
		m = calc[i];
		j = i;
		log(" : New minimum!");
	    } else if (calc[i] == m) {
		log(" : Equal min!");
	    } else {
		log("");
	    }
	}

	log("Closest Grey: mg=" + mg + " (R=" + actual_pal[k].R + ", G=" + actual_pal[k].G + ", B=" + actual_pal[k].B
		+ ")");
	log("Closest Color: m=" + m + " (R=" + actual_pal[j].R + ", G=" + actual_pal[j].G + ", B=" + actual_pal[j].B
		+ ")");

	return j; // (mg < m) ? k : j;
    }

    private void log(String string) {
	System.out.println(string);

    }

    void CalcPaletteFromSettings() {
	int i, j;
	/* Make an RGB table from computed values */
	for (i = 0; i < 0x10; i++) {
	    int r, b;
	    double angle;

	    if (i == 0) {
		r = b = 0;
	    } else {
		angle = Math.PI * (i * (1.0 / 7) - colshift * 0.01);
		r = (int) (Math.cos(angle) * colintens);
		b = (int) (Math.cos(angle - Math.PI * (2.0 / 3)) * colintens);
	    }
	    for (j = 0; j < 0x10; j++) {
		int y, r1, g1, b1;

		y = (max_y * j + min_y * (0xf - j)) / 0xf;
		r1 = y + r;
		g1 = y - r - b;
		b1 = y + b;
		if (r1 > 0xff)
		    r1 = 0xff;
		if (r1 < 0)
		    r1 = 0;
		if (g1 > 0xff)
		    g1 = 0xff;
		if (g1 < 0)
		    g1 = 0;
		if (b1 > 0xff)
		    b1 = 0xff;
		if (b1 < 0)
		    b1 = 0;
		actual_pal[i * 16 + j] = RGB.FromArgb(r1, g1, b1);
	    }
	}
    }

    public void AssignPalette(int[] new_pal) {
	for (int i = 0; i < actual_pal.length; i++) {
	    int c = new_pal[i];
	    int r = (c >> 16) & 255;
	    int g = (c >> 8) & 255;
	    int b = c & 255;
	    actual_pal[i] = RGB.FromArgb(r, g, b);
	}
	CalcLAB();
    }

    private void RGB2LAB(RGB c, LAB lab) {
	double var_R = c.R / 255.0; // R from 0 to 255
	double var_G = c.G / 255.0; // G from 0 to 255
	double var_B = c.B / 255.0; // B from 0 to 255

	if (var_R > 0.04045)
	    var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4);
	else
	    var_R = var_R / 12.92;
	if (var_G > 0.04045)
	    var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4);
	else
	    var_G = var_G / 12.92;
	if (var_B > 0.04045)
	    var_B = Math.pow(((var_B + 0.055) / 1.055), 2.4);
	else
	    var_B = var_B / 12.92;

	var_R = var_R * 100.0;
	var_G = var_G * 100.0;
	var_B = var_B * 100.0;

	// Observer. = 2°, Illuminant = D65
	double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
	double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
	double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;

	double var_X = X / 95.047; // ref_X = 95.047 Observer= 2°, Illuminant=
				   // D65
	double var_Y = Y / 100.000; // ref_Y = 100.000
	double var_Z = Z / 108.883; // ref_Z = 108.883

	if (var_X > 0.008856)
	    var_X = Math.pow(var_X, (1.0 / 3.0));
	else
	    var_X = (7.787 * var_X) + (16.0 / 116.0);
	if (var_Y > 0.008856)
	    var_Y = Math.pow(var_Y, (1.0 / 3.0));
	else
	    var_Y = (7.787 * var_Y) + (16.0 / 116.0);
	if (var_Z > 0.008856)
	    var_Z = Math.pow(var_Z, (1.0 / 3.0));
	else
	    var_Z = (7.787 * var_Z) + (16.0 / 116.0);

	lab.L = (116.0 * var_Y) - 16.0;
	lab.a = 500.0 * (var_X - var_Y);
	lab.b = 200.0 * (var_Y - var_Z);
    }

    private void CalcLAB() {
	for (int i = 0; i < PAL_ENTRIES_NO; i++) {
	    RGB2LAB(actual_pal[i], cielab[i]);
	}
    }

    public Atari8BitPaletteUtility2() {
	AssignPalette(real_pal);
    }

    public void MyColorView_Paint(GC gc) {
	int width = 100;
	int height = 100;

	int nWidth = (width - BAR_ENTRIES_NO) / BAR_ENTRIES_NO;
	int nHeight = (height - BAR_LINES_NO) / BAR_LINES_NO;

	int nOffsetX = (width - (nWidth + 1) * BAR_ENTRIES_NO) / 3 + 1;
	int nOffsetY = (height - (nHeight + 1) * BAR_LINES_NO) / 3 + 1;

	for (int i = 0; i < BAR_LINES_NO; i++) {
	    for (int j = 0; j < BAR_ENTRIES_NO; j++) {
		int offset = i * BAR_ENTRIES_NO + j;
		Rectangle rect = new Rectangle(nOffsetX + j * nWidth + j, nOffsetY + i * nHeight + i, nWidth, nHeight);
		// gc.setForeground(actual_pal[offset]);
		gc.fillRectangle(rect);

		if (offset == highlightIndex) {
		    rect = new Rectangle(rect.x - 1, rect.y - 1, rect.width + 1, rect.height + 1);
		    // gc.setForeground(Color.BLACK);
		    gc.fillRectangle(rect);
		}
	    }
	}
    }
}
