<?php

/*
 * This file is part of Jitamin.
 *
 * Copyright (C) Jitamin Team
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Jitamin\Services\Identity\Avatar;

use Jitamin\Foundation\Base;
use Jitamin\Foundation\Identity\Avatar\AvatarProviderInterface;

/**
 * Letter Avatar Provider.
 *
 * The color hash algorithm is backported from the Javascript library color-hash
 * Source: https://github.com/zenozeng/color-hash
 * Author: Zeno Zeng (MIT License)
 */
class LetterAvatarProvider extends Base implements AvatarProviderInterface
{
    protected $lightness = [0.35, 0.5, 0.65];
    protected $saturation = [0.35, 0.5, 0.65];

    /**
     * Render avatar html.
     *
     * @param array $user
     * @param int   $size
     *
     * @return string
     */
    public function render(array $user, $size)
    {
        $initials = $this->helper->user->getInitials($user['name'] ?: $user['username']);
        $rgb = $this->getBackgroundColor($user['name'] ?: $user['username']);

        return sprintf(
            '<div class="avatar-letter" style="background-color: rgb(%d, %d, %d)" title="%s">%s</div>',
            $rgb[0],
            $rgb[1],
            $rgb[2],
            $this->helper->text->e($user['name'] ?: $user['username']),
            $initials
        );
    }

    /**
     * Determine if the provider is active.
     *
     * @param array $user
     *
     * @return bool
     */
    public function isActive(array $user)
    {
        return true;
    }

    /**
     * Get background color based on a string.
     *
     * @param string $str
     *
     * @return array
     */
    public function getBackgroundColor($str)
    {
        $hsl = $this->getHSL($str);

        return $this->getRGB($hsl[0], $hsl[1], $hsl[2]);
    }

    /**
     * Convert HSL to RGB.
     *
     * @param int $hue        Hue ∈ [0, 360)
     * @param int $saturation Saturation ∈ [0, 1]
     * @param int $lightness  Lightness ∈ [0, 1]
     *
     * @return array
     */
    protected function getRGB($hue, $saturation, $lightness)
    {
        $hue /= 360;
        $q = $lightness < 0.5 ? $lightness * (1 + $saturation) : $lightness + $saturation - $lightness * $saturation;
        $p = 2 * $lightness - $q;

        return array_map(function ($color) use ($q, $p) {
            if ($color < 0) {
                $color++;
            }

            if ($color > 1) {
                $color--;
            }

            if ($color < 1 / 6) {
                $color = $p + ($q - $p) * 6 * $color;
            } elseif ($color < 0.5) {
                $color = $q;
            } elseif ($color < 2 / 3) {
                $color = $p + ($q - $p) * 6 * (2 / 3 - $color);
            } else {
                $color = $p;
            }

            return round($color * 255);
        }, [$hue + 1 / 3, $hue, $hue - 1 / 3]);
    }

    /**
     * Returns the hash in [h, s, l].
     * Note that H ∈ [0, 360); S ∈ [0, 1]; L ∈ [0, 1];.
     *
     * @param string $str
     *
     * @return int[]
     */
    protected function getHSL($str)
    {
        $hash = $this->hash($str);
        $hue = $hash % 359;

        $hash = intval($hash / 360);
        $saturation = $this->saturation[$hash % count($this->saturation)];

        $hash = intval($hash / count($this->saturation));
        $lightness = $this->lightness[$hash % count($this->lightness)];

        return [$hue, $saturation, $lightness];
    }

    /**
     * BKDR Hash (modified version).
     *
     * @param string $str
     *
     * @return int
     */
    protected function hash($str)
    {
        $seed = 131;
        $seed2 = 137;
        $hash = 0;

        // Make hash more sensitive for short string like 'a', 'b', 'c'
        $str .= 'x';
        $max = intval(PHP_INT_MAX / $seed2);

        for ($i = 0, $ilen = mb_strlen($str, 'UTF-8'); $i < $ilen; $i++) {
            if ($hash > $max) {
                $hash = intval($hash / $seed2);
            }

            $hash = $hash * $seed + $this->getCharCode(mb_substr($str, $i, 1, 'UTF-8'));
        }

        return $hash;
    }

    /**
     * Backport of Javascript function charCodeAt().
     *
     * @param string $c
     *
     * @return int
     */
    protected function getCharCode($c)
    {
        list(, $ord) = unpack('N', mb_convert_encoding($c, 'UCS-4BE', 'UTF-8'));

        return $ord;
    }
}
