<?php

/**
 *
 * Color values manipulation utilities. Provides methods to convert from and to
 * Hex, RGB, HSV and HSL color representattions.
 *
 * Several color conversion logic are based on pseudo-code from
 * http://www.easyrgb.com/math.php
 *
 * @category Lux
 *
 * @package  Lux_Color
 *
 * @author   Rodrigo Moraes <rodrigo.moraes@gmail.com>
 *
 * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
 *
 * @version  $Id$
 *
 */
class N2Color {

    static function colorToRGBA($value) {
        $rgba = self::hex2rgba($value);

        return 'RGBA(' . $rgba[0] . ',' . $rgba[1] . ',' . $rgba[2] . ',' . round($rgba[3] / 127, 2) . ')';
    }

    static function hex2alpha($value) {
        return intval(hexdec(substr($value, 6, 2)) / 2);
    }

    static function hex2opacity($value) {
        return self::hex2alpha($value) / 127;
    }

    static function colorToCss($value) {
        return array(
            substr($value, -2) == '00' ? false : substr($value, 0, 6),
            self::colorToRGBA($value)
        );
    }

    static function colorToSVG($value) {
        $rgba = self::hex2rgba($value);

        return array(
            substr($value, 0, 6),
            round($rgba[3] / 127, 2)
        );
    }

    /**
     *
     * Converts hexadecimal colors to RGB.
     *
     * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,
     *                    with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     * @return array RGB values: 0 => R, 1 => G, 2 => B
     *
     */
    static function hex2rgb($hex) {


        // Remove #.
        if (strpos($hex, '#') === 0) {
            $hex = substr($hex, 1);
        }
        if (strlen($hex) == 3) {
            $hex .= $hex;
        }
        if (strlen($hex) != 6) {
            return false;
        }

        // Convert each tuple to decimal.
        $r = hexdec(substr($hex, 0, 2));
        $g = hexdec(substr($hex, 2, 2));
        $b = hexdec(substr($hex, 4, 2));

        return array(
            $r,
            $g,
            $b
        );
    }

    static function hex2rgba($hex) {


        // Remove #.
        if (strpos($hex, '#') === 0) {
            $hex = substr($hex, 1);
        }
        if (strlen($hex) == 6) {
            $hex .= 'ff';
        }
        if (strlen($hex) != 8) {
            return false;
        }

        // Convert each tuple to decimal.
        $r = hexdec(substr($hex, 0, 2));
        $g = hexdec(substr($hex, 2, 2));
        $b = hexdec(substr($hex, 4, 2));
        $a = intval(hexdec(substr($hex, 6, 2)) / 2);

        return array(
            $r,
            $g,
            $b,
            $a
        );
    }

    static function hex82hex($hex) {


        // Remove #.
        if (strpos($hex, '#') === 0) {
            $hex = substr($hex, 1);
        }
        if (strlen($hex) == 6) {
            $hex .= 'ff';
        }
        if (strlen($hex) != 8) {
            return false;
        }

        return array(
            substr($hex, 0, 6),
            substr($hex, 6, 2)
        );
    }

    /**
     *
     * Converts hexadecimal colors to HSV.
     *
     * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,
     *                    with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     * @return array HSV values: 0 => H, 1 => S, 2 => V
     *
     */
    function hex2hsv($hex) {

        return $this->rgb2hsv($this->hex2rgb($hex));
    }

    /**
     *
     * Converts hexadecimal colors to HSL.
     *
     * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,
     *                    with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     * @return array HSL values: 0 => H, 1 => S, 2 => L
     *
     */
    static function hex2hsl($hex) {

        return self::rgb2hsl(self::hex2rgb($hex));
    }

    /**
     *
     * Converts RGB colors to hexadecimal.
     *
     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
     *
     * @return string Hexadecimal value with six digits, e.g., CCCCCC.
     *
     */
    function rgb2hex($rgb) {

        if (count($rgb) < 3) {
            return false;
        }
        list($r, $g, $b) = $rgb;

        // From php.net.
        $r = 0x10000 * max(0, min(255, $r));
        $g = 0x100 * max(0, min(255, $g));
        $b = max(0, min(255, $b));

        return strtoupper(str_pad(dechex($r + $g + $b), 6, 0, STR_PAD_LEFT));
    }

    /**
     *
     * Converts RGB to HSV.
     *
     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
     *
     * @return array HSV values: 0 => H, 1 => S, 2 => V
     *
     */
    function rgb2hsv($rgb) {


        // RGB values = 0 ?? 255
        $var_R = ($rgb[0] / 255);
        $var_G = ($rgb[1] / 255);
        $var_B = ($rgb[2] / 255);

        // Min. value of RGB
        $var_Min = min($var_R, $var_G, $var_B);

        // Max. value of RGB
        $var_Max = max($var_R, $var_G, $var_B);

        // Delta RGB value
        $del_Max = $var_Max - $var_Min;
        $V       = $var_Max;

        // This is a gray, no chroma...
        if ($del_Max == 0) {

            // HSV results = 0 ?? 1
            $H = 0;
            $S = 0;
        } else {

            // Chromatic data...
            $S     = $del_Max / $var_Max;
            $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;
            $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;
            $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;
            if ($var_R == $var_Max) {
                $H = $del_B - $del_G;
            } else if ($var_G == $var_Max) {
                $H = (1 / 3) + $del_R - $del_B;
            } else if ($var_B == $var_Max) {
                $H = (2 / 3) + $del_G - $del_R;
            }
            if ($H < 0) {
                $H += 1;
            }
            if ($H > 1) {
                $H -= 1;
            }
        }

        // Returns agnostic values.
        // Range will depend on the application: e.g. $H*360, $S*100, $V*100.

        return array(
            $H,
            $S,
            $V
        );
    }

    /**
     *
     * Converts RGB to HSL.
     *
     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
     *
     * @return array HSL values: 0 => H, 1 => S, 2 => L
     *
     */
    static function rgb2hsl($rgb) {


        // Where RGB values = 0 ?? 255.
        $var_R = $rgb[0] / 255;
        $var_G = $rgb[1] / 255;
        $var_B = $rgb[2] / 255;

        // Min. value of RGB
        $var_Min = min($var_R, $var_G, $var_B);

        // Max. value of RGB
        $var_Max = max($var_R, $var_G, $var_B);

        // Delta RGB value
        $del_Max = $var_Max - $var_Min;
        $L       = ($var_Max + $var_Min) / 2;
        if ($del_Max == 0) {

            // This is a gray, no chroma...
            // HSL results = 0 ?? 1

            $H = 0;
            $S = 0;
        } else {

            // Chromatic data...
            if ($L < 0.5) {
                $S = $del_Max / ($var_Max + $var_Min);
            } else {
                $S = $del_Max / (2 - $var_Max - $var_Min);
            }
            $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;
            $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;
            $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;
            if ($var_R == $var_Max) {
                $H = $del_B - $del_G;
            } else if ($var_G == $var_Max) {
                $H = (1 / 3) + $del_R - $del_B;
            } else if ($var_B == $var_Max) {
                $H = (2 / 3) + $del_G - $del_R;
            }
            if ($H < 0) {
                $H += 1;
            }
            if ($H > 1) {
                $H -= 1;
            }
        }

        return array(
            $H,
            $S,
            $L
        );
    }

    /**
     *
     * Converts HSV colors to hexadecimal.
     *
     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
     *
     * @return string Hexadecimal value with six digits, e.g., CCCCCC.
     *
     */
    function hsv2hex($hsv) {

        return $this->rgb2hex($this->hsv2rgb($hsv));
    }

    /**
     *
     * Converts HSV to RGB.
     *
     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
     *
     * @return array RGB values: 0 => R, 1 => G, 2 => B
     *
     */
    function hsv2rgb($hsv) {

        $H = $hsv[0];
        $S = $hsv[1];
        $V = $hsv[2];

        // HSV values = 0 ?? 1
        if ($S == 0) {
            $R = $V * 255;
            $G = $V * 255;
            $B = $V * 255;
        } else {
            $var_h = $H * 6;

            // H must be < 1
            if ($var_h == 6) {
                $var_h = 0;
            }

            // Or ... $var_i = floor( $var_h )
            $var_i = floor($var_h);
            $var_1 = $V * (1 - $S);
            $var_2 = $V * (1 - $S * ($var_h - $var_i));
            $var_3 = $V * (1 - $S * (1 - ($var_h - $var_i)));
            switch ($var_i) {
                case 0:
                    $var_r = $V;
                    $var_g = $var_3;
                    $var_b = $var_1;
                    break;
                case 1:
                    $var_r = $var_2;
                    $var_g = $V;
                    $var_b = $var_1;
                    break;
                case 2:
                    $var_r = $var_1;
                    $var_g = $V;
                    $var_b = $var_3;
                    break;
                case 3:
                    $var_r = $var_1;
                    $var_g = $var_2;
                    $var_b = $V;
                    break;
                case 4:
                    $var_r = $var_3;
                    $var_g = $var_1;
                    $var_b = $V;
                    break;
                default:
                    $var_r = $V;
                    $var_g = $var_1;
                    $var_b = $var_2;
            }

            //RGB results = 0 ?? 255
            $R = $var_r * 255;
            $G = $var_g * 255;
            $B = $var_b * 255;
        }

        return array(
            $R,
            $G,
            $B
        );
    }

    /**
     *
     * Converts HSV colors to HSL.
     *
     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
     *
     * @return array HSL values: 0 => H, 1 => S, 2 => L
     *
     */
    function hsv2hsl($hsv) {

        return $this->rgb2hsl($this->hsv2rgb($hsv));
    }

    /**
     *
     * Converts hexadecimal colors to HSL.
     *
     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
     *
     * @return string Hexadecimal value. Accepts values with 3 or 6 numbers,
     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     */
    function hsl2hex($hsl) {

        return $this->rgb2hex($this->hsl2rgb($hsl));
    }

    /**
     *
     * Converts HSL to RGB.
     *
     * @param array $hsv HSL values: 0 => H, 1 => S, 2 => L
     *
     * @return array RGB values: 0 => R, 1 => G, 2 => B
     *
     */
    static function hsl2rgb($hsl) {

        list($H, $S, $L) = $hsl;
        if ($S == 0) {

            // HSL values = 0 ?? 1
            // RGB results = 0 ?? 255

            $R = $L * 255;
            $G = $L * 255;
            $B = $L * 255;
        } else {
            if ($L < 0.5) {
                $var_2 = $L * (1 + $S);
            } else {
                $var_2 = ($L + $S) - ($S * $L);
            }
            $var_1 = 2 * $L - $var_2;
            $R     = 255 * self::_hue2rgb($var_1, $var_2, $H + (1 / 3));
            $G     = 255 * self::_hue2rgb($var_1, $var_2, $H);
            $B     = 255 * self::_hue2rgb($var_1, $var_2, $H - (1 / 3));
        }

        return array(
            $R,
            $G,
            $B
        );
    }

    /**
     *
     * Support method for hsl2rgb(): converts hue ro RGB.
     *
     * @param
     *
     * @param
     *
     * @param
     *
     * @return int
     *
     */
    static function _hue2rgb($v1, $v2, $vH) {

        if ($vH < 0) {
            $vH += 1;
        }
        if ($vH > 1) {
            $vH -= 1;
        }
        if ((6 * $vH) < 1) {
            return ($v1 + ($v2 - $v1) * 6 * $vH);
        }
        if ((2 * $vH) < 1) {
            return $v2;
        }
        if ((3 * $vH) < 2) {
            return ($v1 + ($v2 - $v1) * ((2 / 3) - $vH) * 6);
        }

        return $v1;
    }

    /**
     *
     * Converts hexadecimal colors to HSL.
     *
     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
     *
     * @return array HSV values: 0 => H, 1 => S, 2 => V
     *
     */
    function hsl2hsv($hsl) {

        return $this->rgb2hsv($this->hsl2rgb($hsl));
    }

    /**
     *
     * Updates HSV values.
     *
     * @param array $hsv    HSV values: 0 => H, 1 => S, 2 => V
     *
     * @param array $values Values to update: 0 => value to add to H (0 to 360),
     *                      1 and 2 => values to multiply S and V (0 to 100). Example:
     *
     * {{{code:php
     *     // Update saturation to 80% in the provided HSV.
     *     $hsv = array(120, 0.75, 0.75);
     *     $new_hsv = $color->updateHsv($hsv, array(null, 80, null));
     * }}}
     *
     */
    function updateHsv($hsv, $values) {

        if (isset($values[0])) {
            $hsv[0] = max(0, min(360, ($hsv[0] + $values[0])));
        }
        if (isset($values[1])) {
            $hsv[1] = max(0, min(1, ($hsv[1] * ($values[1] / 100))));
        }
        if (isset($values[2])) {
            $hsv[2] = max(0, min(1, ($hsv[2] * ($values[2] / 100))));
        }

        return $hsv;
    }

    /**
     *
     * Updates HSL values.
     *
     * @param array $hsl    HSL values: 0 => H, 1 => S, 2 => L
     *
     * @param array $values Values to update: 0 => value to add to H (0 to 360),
     *                      1 and 2 => values to multiply S and V (0 to 100). Example:
     *
     * {{{code:php
     *     // Update saturation to 80% in the provided HSL.
     *     $hsl = array(120, 0.75, 0.75);
     *     $new_hsl = $color->updateHsl($hsl, array(null, 80, null));
     * }}}
     *
     */
    function updateHsl($hsl, $values) {

        if (isset($values[0])) {
            $hsl[0] = max(0, min(1, ($hsl[0] + $values[0] / 360)));
        }
        if (isset($values[1])) {
            $hsl[1] = max(0, min(1, ($hsl[1] * ($values[1] / 100))));
        }
        if (isset($values[2])) {
            $hsl[2] = max(0, min(1, ($hsl[2] * ($values[2] / 100))));
        }

        return $hsl;
    }

    static function rgb2array($rgb) {

        return array(
            base_convert(substr($rgb, 0, 2), 16, 10),
            base_convert(substr($rgb, 2, 2), 16, 10),
            base_convert(substr($rgb, 4, 2), 16, 10),
        );
    }

}