package com.guigarage.lessfx.converters.color.definition;

import com.guigarage.lessfx.converters.LessStyleConverter;
import javafx.css.ParsedValue;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;

import java.util.regex.Matcher;

/**
 * @author Robin Küster
 * @version 1.0-SNAPSHOT
 * @since 2015-03-03
 */
public class HSLConverter extends LessStyleConverter<String, Color> {
    /**
     * Regular Expression to parse the function call.
     */
    private final static String REGEX = "^hsl\\(([0-9]{1,3}),\\s*((?:[0-1]\\.[0-9]*)|(?:[0-9]{1,3}%)),\\s*((?:[0-1]\\.[0-9]*)|(?:[0-9]{1,3}%))\\)$";

    /**
     * Initialization-on-demand holder
     */
    private static class Holder {
        static final HSLConverter INSTANCE = new HSLConverter();
    }

    /**
     * Instance of the converter that got initialize in the Holder.
     *
     * @return Instance of the converter.
     */
    public static HSLConverter getInstance() {
        return Holder.INSTANCE;
    }

    /**
     * Converts the given CSS function call to a Java object.
     *
     * @param value ParsedValue containing the function call
     * @param font Font used for functions that depend on the font size
     * @return The result of the function call as a Java object or null if function call failed
     */
    @Override
    public Color convert(ParsedValue<String, Color> value, Font font) {
        Matcher matcher = getMatcher(value.getValue(), REGEX);

        // nonsensical input
        if (matcher == null) {
            return null;
        }

        int h = Integer.valueOf(matcher.group(1));
        if (h > 360 || h < 0) {
            return null;
        }

        double color[] = new double[2];

        for (int i = 2; i <= 3; i++) {
            String group = matcher.group(i);
            if (group.charAt(group.length() - 1) == '%') { // value given in percentage
                color[i-2] = Double.valueOf(group.substring(0, group.length() - 1)) / 100;
            } else { // value between 0 and 1
                color[i-2] = Double.valueOf(group);
            }
            if (color[i-2] > 1.0) {
                return null;
            }
        }

        /*
        Algorithm for RGB to HSL conversion explained here: http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
         */

        double c = (1 - Math.abs(2*color[1] - 1)) * color[0];
        double h2 = h / 60;
        double x = c * (1-Math.abs((h2 % 2) - 1));

        double r, g, b;

        if (h2 < 1) {
            r = c;
            g = x;
            b = 0;
        } else  if (h2 >= 1 && h2 < 2) {
            r = x;
            g = c;
            b = 0;
        } else if (h2 >= 2 && h2 < 3) {
            r = 0;
            g = c;
            b = x;
        } else if (h2 >= 3 && h2 < 4) {
            r = 0;
            g = x;
            b = c;
        } else if (h2 >= 4 && h2 < 5) {
            r = x;
            g = 0;
            b = c;
        } else if (h2 >= 5 && h2 < 6) {
            r = c;
            g = 0;
            b = x;
        } else {
            r = g = b = 0;
        }

        double m = color[1] - 0.5 * c;

        return new Color(r + m, g + m, b + m, 1.0);
    }
}

