/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Controller;

/**
 *
 * @author 1320035
 */
import java.util.ArrayList;

import java.awt.Color;
import java.util.Formatter;
import java.util.Random;
public class Generate_100_Random_Colors {

    private final static float U_OFF = .436f,
            V_OFF = .615f;
    private static long RAND_SEED = 0;      // final keyword has been removed for SAST
    private static Random rand = new Random(RAND_SEED);

    public static void hsv2rgb(float h, float s, float v, float[] rgb) {
        // H is given on [0->6] or -1. S and V are given on [0->1].
        // RGB are each returned on [0->1].
        float m, n, f;
        int i;

        float[] hsv = new float[3];

        hsv[0] = h;
        hsv[1] = s;
        hsv[2] = v;
        System.out.println("H: " + h + " S: " + s + " V:" + v);
        if (hsv[0] == -1) {
            rgb[0] = rgb[1] = rgb[2] = hsv[2];
            return;
        }
        i = (int) (Math.floor(hsv[0]));
        f = hsv[0] - i;
        if (i % 2 == 0) {
            f = 1 - f; // if i is even
        }
        m = hsv[2] * (1 - hsv[1]);
        n = hsv[2] * (1 - hsv[1] * f);
        switch (i) {
            case 6:
            case 0:
                rgb[0] = hsv[2];
                rgb[1] = n;
                rgb[2] = m;
                break;
            case 1:
                rgb[0] = n;
                rgb[1] = hsv[2];
                rgb[2] = m;
                break;
            case 2:
                rgb[0] = m;
                rgb[1] = hsv[2];
                rgb[2] = n;
                break;
            case 3:
                rgb[0] = m;
                rgb[1] = n;
                rgb[2] = hsv[2];
                break;
            case 4:
                rgb[0] = n;
                rgb[1] = m;
                rgb[2] = hsv[2];
                break;
            case 5:
                rgb[0] = hsv[2];
                rgb[1] = m;
                rgb[2] = n;
                break;
        }
    }

    public static void yuv2rgb(float y, float u, float v, float[] rgb) {
        rgb[0] = 1 * y + 0 * u + 1.13983f * v;
        rgb[1] = 1 * y + -.39465f * u + -.58060f * v;
        rgb[2] = 1 * y + 2.03211f * u + 0 * v;
    }

    public static void rgb2yuv(float r, float g, float b, float[] yuv) {
        yuv[0] = .299f * r + .587f * g + .114f * b;
        yuv[1] = -.14713f * r + -.28886f * g + .436f * b;
        yuv[2] = .615f * r + -.51499f * g + -.10001f * b;
    }

    private static float[] randYUVinRGBRange(float minComponent, float maxComponent) {
        while (true) {
            float y = rand.nextFloat(); // * YFRAC + 1-YFRAC);
            float u = rand.nextFloat() * 2 * U_OFF - U_OFF;
            float v = rand.nextFloat() * 2 * V_OFF - V_OFF;
            float[] rgb = new float[3];
            yuv2rgb(y, u, v, rgb);
            float r = rgb[0], g = rgb[1], b = rgb[2];
            if (0 <= r && r <= 1
                    && 0 <= g && g <= 1
                    && 0 <= b && b <= 1
                    && (r > minComponent || g > minComponent || b > minComponent) && // don't want all dark components
                    (r < maxComponent || g < maxComponent || b < maxComponent)) // don't want all light components
            {
                return new float[]{y, u, v};
            }
        }
    }

    /*
     * Returns an array of ncolors RGB triplets such that each is as unique from the rest as possible
     * and each color has at least one component greater than minComponent and one less than maxComponent.
     * Use min == 1 and max == 0 to include the full RGB color range.
     *
     * Warning: O N^2 algorithm blows up fast for more than 100 colors.
     */
    public static Color[] generateVisuallyDistinctColors(int ncolors, float minComponent, float maxComponent) {
        rand.setSeed(RAND_SEED); // So that we get consistent results for each combination of inputs

        float[][] yuv = new float[ncolors][3];

        // initialize array with random colors
        for (int got = 0; got < ncolors;) {
            System.arraycopy(randYUVinRGBRange(minComponent, maxComponent), 0, yuv[got++], 0, 3);
        }
        // continually break up the worst-fit color pair until we get tired of searching
        for (int c = 0; c < ncolors * 1000; c++) {
            float worst = 8888;
            int worstID = 0;
            for (int i = 1; i < yuv.length; i++) {
                for (int j = 0; j < i; j++) {
                    float dist = sqrdist(yuv[i], yuv[j]);
                    if (dist < worst) {
                        worst = dist;
                        worstID = i;
                    }
                }
            }
            float[] best = randYUVBetterThan(worst, minComponent, maxComponent, yuv);
            if (best == null) {
                break;
            } else {
                yuv[worstID] = best;
            }
        }

        Color[] rgbs = new Color[yuv.length];
        for (int i = 0; i < yuv.length; i++) {
            float[] rgb = new float[3];
            yuv2rgb(yuv[i][0], yuv[i][1], yuv[i][2], rgb);
            rgbs[i] = new Color(rgb[0], rgb[1], rgb[2]);
            //System.out.println(rgb[i][0] + "\t" + rgb[i][1] + "\t" + rgb[i][2]);
        }

        return rgbs;
    }

    private static float sqrdist(float[] a, float[] b) {
        float sum = 0;
        for (int i = 0; i < a.length; i++) {
            float diff = a[i] - b[i];
            sum += diff * diff;
        }
        return sum;
    }

    private static double worstFit(Color[] colors) {
        float worst = 8888;
        float[] a = new float[3], b = new float[3];
        for (int i = 1; i < colors.length; i++) {
            colors[i].getColorComponents(a);
            for (int j = 0; j < i; j++) {
                colors[j].getColorComponents(b);
                float dist = sqrdist(a, b);
                if (dist < worst) {
                    worst = dist;
                }
            }
        }
        return Math.sqrt(worst);
    }

    private static float[] randYUVBetterThan(float bestDistSqrd, float minComponent, float maxComponent, float[][] in) {
        for (int attempt = 1; attempt < 100 * in.length; attempt++) {
            float[] candidate = randYUVinRGBRange(minComponent, maxComponent);
            boolean good = true;
            for (int i = 0; i < in.length; i++) {
                if (sqrdist(candidate, in[i]) < bestDistSqrd) {
                    good = false;
                }
            }
            if (good) {
                return candidate;
            }
        }
        return null; // after a bunch of passes, couldn't find a candidate that beat the best.
    }

    public ArrayList<String> genRandColors() {
        final int ncolors = 100;
        ArrayList<String> randColors = null;
        try {
            randColors = new ArrayList<String>();
            Color[] colors = generateVisuallyDistinctColors(ncolors, .8f, .3f);
            for (int i = 0; i < colors.length; i++) {
                Formatter f = new Formatter(new StringBuffer("#"));
                f.format("%02X", Integer.parseInt(colors[i].toString().split(",g")[0].split("=")[1]));
                f.format("%02X", Integer.parseInt(colors[i].toString().split(",b")[0].split(",g=")[1]));
                f.format("%02X", Integer.parseInt(colors[i].toString().split(",b=")[1].split("]")[0]));
                randColors.add(f.toString());
            }
        } catch (Exception ex) {
           // ex.printStackTrace();
           System.out.println("Error Occurred during processing.");
        } finally {
           // return randColors;
           System.out.println("Processing done.");
        }
        return randColors;
    }

}
