<?php

/**
 * Joomla! Content Management System
 *
 * @copyright   (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\CMS\Image\Filter;

use Joomla\CMS\Image\ImageFilter;

// phpcs:disable PSR1.Files.SideEffects
\defined('JPATH_PLATFORM') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Image Filter class fill background with color;
 *
 * @since       3.4
 */
class Backgroundfill extends ImageFilter
{
    /**
     * Method to apply a background color to an image resource.
     *
     * @param   array  $options  An array of options for the filter.
     *                           color  Background matte color
     *
     * @return  void
     *
     * @since   3.4
     * @throws  \InvalidArgumentException
     */
    public function execute(array $options = [])
    {
        // Validate that the color value exists and is an integer.
        if (!isset($options['color'])) {
            throw new \InvalidArgumentException('No color value was given. Expected string or array.');
        }

        $colorCode = $options['color'] ?? null;

        // Get resource dimensions
        $width  = imagesx($this->handle);
        $height = imagesy($this->handle);

        // Sanitize color
        $rgba = $this->sanitizeColor($colorCode);

        // Enforce alpha on source image
        if (imageistruecolor($this->handle)) {
            imagealphablending($this->handle, false);
            imagesavealpha($this->handle, true);
        }

        // Create background
        $bg = imagecreatetruecolor($width, $height);
        imagesavealpha($bg, empty($rgba['alpha']));

        // Allocate background color.
        $color = imagecolorallocatealpha($bg, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']);

        // Fill background
        imagefill($bg, 0, 0, $color);

        // Apply image over background
        imagecopy($bg, $this->handle, 0, 0, 0, 0, $width, $height);

        // Move flattened result onto current handle.
        // If handle was palette-based, it'll stay like that.
        imagecopy($this->handle, $bg, 0, 0, 0, 0, $width, $height);

        // Free up memory
        imagedestroy($bg);
    }

    /**
     * Method to sanitize color values and/or convert to an array
     *
     * @param   mixed  $input  Associative array of colors and alpha, or hex RGBA string when alpha FF is opaque. Defaults to black and opaque alpha.
     *
     * @return  array  Associative array of red, green, blue and alpha
     *
     * @since   3.4
     *
     * @note    '#FF0000FF' returns an array with alpha of 0 (opaque)
     */
    protected function sanitizeColor($input)
    {
        // Construct default values
        $colors = ['red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0];

        // Make sure all values are in
        if (\is_array($input)) {
            $colors = array_merge($colors, $input);
        } elseif (\is_string($input)) {
            // Convert RGBA 6-9 char string
            $hex = ltrim($input, '#');

            $hexValues = [
                'red'   => substr($hex, 0, 2),
                'green' => substr($hex, 2, 2),
                'blue'  => substr($hex, 4, 2),
                'alpha' => substr($hex, 6, 2),
            ];

            $colors = array_map('hexdec', $hexValues);

            // Convert Alpha to 0..127 when provided
            if (\strlen($hex) > 6) {
                $colors['alpha'] = floor((255 - $colors['alpha']) / 2);
            }
        } else {
            // Cannot sanitize such type
            return $colors;
        }

        // Make sure each value is within the allowed range
        foreach ($colors as &$value) {
            $value = max(0, min(255, (int) $value));
        }

        $colors['alpha'] = min(127, $colors['alpha']);

        return $colors;
    }
}
