package vismon.util;

import vismon.data.MngOption;

import java.awt.*;
import java.util.Formatter;
import java.util.List;
import java.util.ArrayList;

public class Util {

    public static boolean isNaN(double d) {
        return d != d;
    }

    public static boolean equals(double a, double b) {
        return (a - b <= 1e-5 && b - a <= 1e-5);
    }

    public static double roundToOneTenth(double d) {
        return Math.round(d * 10) / 10.0;
    }

    public static double roundToOneHundredth(double d) {
        return Math.round(d * 100) / 100.0;
    }

    public static double roundToOneThousendth(double d) {
        return Math.round(d * 1000) / 1000.0;
    }

    public static List<double[]> getPermutations(List<MngOption> mngOptions) {
        if (mngOptions == null || mngOptions.size() == 0)
            return null;

        if (mngOptions.size() == 1) {
            List<double[]> output = new ArrayList<double[]>();
            for (double v : mngOptions.get(0).getValues())
                output.add(new double[]{v});
            return output;
        }

        MngOption firstDim = mngOptions.get(0);
        List<MngOption> restDims = new ArrayList<MngOption>();
        for (int i = 1; i < mngOptions.size(); ++i)
            restDims.add(mngOptions.get(i));
        List<double[]> restPermutations = getPermutations(restDims);

        List<double[]> output = new ArrayList<double[]>();

        for (int i = 0; i < restPermutations.size(); ++i) {
            double[] restPerm = restPermutations.get(i);
            for (double value : firstDim.getValues()) {
                double[] perm = new double[mngOptions.size()];
                perm[0] = value;
                for (int j = 0; j < restPerm.length; ++j)
                    perm[j + 1] = restPerm[j];
                output.add(perm);
            }
        }
        return output;
    }

    public static List<double[]> getPermutations(MngOption mngOption0, MngOption mngOption1) {
        List<double[]> output = new ArrayList<double[]>();

        for (int j = 0; j < mngOption1.getValueNum(); ++j) {
            for (int i = 0; i < mngOption0.getValueNum(); ++i) {
                double[] perm = new double[]{mngOption0.getValue(i), mngOption1.getValue(j)};
                output.add(perm);
            }
        }
        return output;
    }

    public static String fontToString(Font f) {
        String fontString = f.getFamily() + ", ";
        switch (f.getStyle()) {
            case 0:
                fontString += "PLAIN";
                break;
            case 1:
                fontString += "BOLD";
                break;
            case 2:
                fontString += "ITALIC";
                break;
            default:
                fontString += "PlAIN";
                break;
        }
        fontString += ", " + f.getSize();
        return fontString;
    }

    private final static double FIXED_TERM = Math.sqrt(0.5 / Math.PI);

    public static double getGaussian(double y_n, double mean, double sd) {
        return (FIXED_TERM / sd) * Math.exp(-((y_n - mean) * (y_n - mean)) / (2 * sd * sd));
    }

    public static Color getDarkerColor(Color c) {
        return getDarkerColor(c, 0.9f);
    }

    public static Color getDarkerColor(Color c, float percent) {
        float[] hsb = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
        float h = hsb[0];
        float s = hsb[1];
        float b = hsb[2];

        float newB = percent * b;

        int rgb = Color.HSBtoRGB(h, s, newB);
        int red = (rgb >> 16) & 0xFF;
        int green = (rgb >> 8) & 0xFF;
        int blue = rgb & 0xFF;
        return new Color(red, green, blue);
    }

    public static Color getBrighterColor(Color c, double percent) {
        int cRed = c.getRed();
        int cGreen = c.getGreen();
        int cBlue = c.getBlue();

        int red = (int) ((255-cRed)*percent + cRed);
        int green = (int) ((255-cGreen)*percent + cGreen);
        int blue = (int) ((255-cBlue)*percent + cBlue);

        return new Color(red, green, blue);
    }

    public static void drawStringHorCentre(Graphics2D g, String label, int xCentre, int yCentre) {
        int[] stringWH = getStringWH(g, label, g.getFont());
        int stringWidth = stringWH[0];
        int stringHeight = stringWH[1];
        g.drawString(label, xCentre - (int) (0.5 * stringWidth), yCentre + (int) (0.5 * stringHeight));
    }

    public static void drawStringHorRight(Graphics2D g, String label, int x, int y) {
        int[] stringWH = getStringWH(g, label, g.getFont());
        int stringWidth = stringWH[0];
        int stringHeight = stringWH[1];
        g.drawString(label, x - stringWidth, y + (int) (0.5 * stringHeight));
    }

    public static void drawStringVerTop(Graphics2D g, String label, int x, int y) {
        int[] stringWH = getStringWH(g, label, g.getFont());
        int stringWidth = stringWH[0];
        int stringHeight = stringWH[1];
        g.rotate(-Math.PI * 0.5, x, y);
        g.drawString(label, x - stringWidth, y + (int) (0.5 * stringHeight));
        g.rotate(Math.PI * 0.5, x, y);
    }

    public static void drawStringVerCentre(Graphics2D g, String label, int x, int y) {
        int[] stringWH = getStringWH(g, label, g.getFont());
        int stringWidth = stringWH[0];
        int stringHeight = stringWH[1];
        g.rotate(-Math.PI * 0.5, x, y);
        g.drawString(label, x - (int) (0.5 * stringWidth), y + (int) (0.5 * stringHeight));
        g.rotate(Math.PI * 0.5, x, y);
    }

    public static void drawStringVerCentreRight(Graphics2D g, String label, int x, int y) {
        int[] stringWH = getStringWH(g, label, g.getFont());
        int stringWidth = stringWH[0];
        int stringHeight = stringWH[1];
        g.rotate(-Math.PI * 0.5, x, y);
        g.drawString(label, x - (int) (0.5 * stringWidth), y/* - stringHeight*/);
        g.rotate(Math.PI * 0.5, x, y);
    }

    public static int[] getStringWH(Graphics2D g, String s, Font f) {
        return new int[]{getStringWidth(g, s, f), getStringHeight(g, s, f)};
    }

    public static int getStringWidth(Graphics2D g, String s, Font f) {
        FontMetrics fMetrics = g.getFontMetrics(f);
        return fMetrics.stringWidth(s);
    }

    public static int getStringHeight(Graphics2D g, String s, Font f) {
        FontMetrics fMetrics = g.getFontMetrics(f);
        return fMetrics.getHeight();
    }

    public static void drawStringCentre(Graphics2D g, String s, double rotationAngle, int x, int y) {
        int[] stringWH = getStringWH(g, s, g.getFont());
        int stringWidth = stringWH[0];
        int stringHeight = stringWH[1];
        if (rotationAngle == 0.0)
            g.drawString(s, x - (int)(0.5 * stringWidth), y + (int)(0.5 * stringHeight));
        else {
            g.rotate(-rotationAngle, x, y);
            g.drawString(s, x - (int)(0.5 * stringWidth), y + (int)(0.5 * stringHeight));
            g.rotate(rotationAngle, x, y);
        }
    }

    public static double normalizeAngle(double radian) {
        while (radian > 0.5 * Math.PI)
            radian -= Math.PI;
        while (radian < -0.5 * Math.PI)
            radian += Math.PI;
        return radian;
    }

    public static double calculateAngle(int xMouse, int yMouse, int xCentre, int yCentre) {
        double angle = -Math.atan2(xMouse - xCentre, yMouse - yCentre) + 0.5 * Math.PI;
        if (angle < 0)
            angle += 2 * Math.PI;
        return angle;
    }

    private static final int[] tens = {1, 10, 100, 1000, 10000, 100000, 1000000};
    public static String format(double d, int precision) {
        int round = (int)(d * tens[precision] + 0.5);
        while (precision > 0 && round % 10 == 0) {
            --precision;
            round /= 10;
        }
        double dd = (double)round / tens[precision];
        return new Formatter().format("%." + precision + "f", dd).toString();
    }

    public static Color getBarBGColor(int i) {
        int rbg = 255 - i * 10;
        return new Color(rbg, rbg, rbg);
    }
}
