<?php

/**
 * @copyright
 * @package     RIE - Random Image Extended for Joomla! 3.x
 * @author      Viktor Vogel <admin@kubik-rubik.de>
 * @version     3.4.0-FREE - 2020-09-01
 * @link        https://kubik-rubik.de/rie-random-image-extended
 *
 * @license     GNU/GPL
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

namespace RandomImageExtended;

defined('_JEXEC') || die('Restricted access');

use Joomla\CMS\{Factory, Uri\Uri, HTML\HTMLHelper};
use Joomla\String\StringHelper;

class Helper
{
    /**
     * Gets the image for the output and prepare other images for the gallery if selected
     *
     * @param object $params
     * @param array  $images
     * @param int    $width
     * @param int    $height
     * @param bool   $imageRotator
     * @param array  $linksInfo
     *
     * @return mixed
     * @since 3.4.0-FREE
     */
    public function getRandomImages(object $params, array &$images, int $width, int $height, bool $imageRotator, array $linksInfo = [])
    {
        $imagesDisplay = [];
        $ratio = (bool)$params->get('ratio', true);
        $linkFolder = (bool)$params->get('linkFolder', false);
        $overwrite = (bool)$params->get('overwrite', false);
        $allPicsRandom = (int)$params->get('allPicsRandom', 1);
        $allPics = (bool)$params->get('allPics', true);
        $quantity = (int)$params->get('quantity', 1);

        if ($quantity > count($images)) {
            $quantity = count($images);
        }

        $this->setImageProperties($images, $linksInfo);

        if (!empty($allPicsRandom)) {
            if ($allPicsRandom == 2 && $allPics == 1) {
                $image = $images[0];
                unset($images[0]);
            } else {
                $random = mt_rand(0, count($images) - 1);
                $image = $images[$random];
                unset($images[$random]);
            }

            $imagesDisplay[] = $image;
            $quantity--;

            if ($allPics == 1) {
                shuffle($images);
            } else {
                $images = array_values($images);
            }
        }

        if ($quantity >= 1 && empty($imageRotator)) {
            for ($i = 0; $i < $quantity; $i++) {
                $imagesDisplay[] = $images[$i];
                unset($images[$i]);
            }
        }

        foreach ($imagesDisplay as &$image) {
            // Calculate correct image size
            $this->calculateImageSize($image, $ratio, $width, $height);

            if ($linkFolder) {
                $this->createThumbnails($image, $overwrite);
            }
        }

        if ($imageRotator) {
            foreach ($images as &$imageTail) {
                $this->calculateImageSize($imageTail, $ratio, $width, $height);

                if ($linkFolder) {
                    $this->createThumbnails($imageTail, $overwrite);
                }
            }
        }

        return $imagesDisplay;
    }

    /**
     * Sets the properties of the images from the text file if provided
     * Private function - called in the function getRandomImage
     *
     * @param array $images
     * @param array $linkInfo
     *
     * @since 3.4.0-FREE
     */
    private function setImageProperties(array &$images, array $linkInfo)
    {
        if (!empty($linkInfo)) {
            $imagesInfo = [];

            foreach ($linkInfo as $imageName => $imageInfo) {
                foreach ($images as $key => $image) {
                    if ($imageName == $image->link) {
                        $image->name = $imageInfo[0];
                        $image->linkto = $imageInfo[1];

                        $imagesInfo[] = $image;

                        // Unset this image in the images array to avoid unnecessary loops
                        unset($images[$key]);
                        break;
                    }
                }
            }

            if (!empty($imageInfo)) {
                $images = array_merge($imagesInfo, $images);
            }
        }

        // Set the needed properties for images with no entry in the text file or if the text file is not used at all
        foreach ($images as &$image) {
            // If name is not set, then generate it from the image name - remove extensions of the file
            // name, remove special characters and uppercase the first character of each word in a string
            if (empty($image->name)) {
                $image->name = htmlspecialchars(ucwords(str_replace(['_', '-', '+', '=', '#', '?'], ' ', substr($image->link, 0, -4))));
            }

            // Linkto property is set to an empty string
            if (!isset($image->linkto)) {
                $image->linkto = '';
            }
        }
    }

    /**
     * Calculates the image size
     *
     * @param object $image
     * @param bool   $ratio
     * @param int    $width
     * @param int    $height
     *
     * @since 3.4.0-FREE
     */
    private function calculateImageSize(object &$image, bool $ratio, int $width, int $height)
    {
        $size = getimagesize(JPATH_BASE . '/' . $image->folder . '/' . $image->link);

        if ($ratio || empty($width) || empty($height)) {
            if (empty($width)) {
                $width = 200;
            }

            if ($size[0] < $width) {
                $width = $size[0];
            }

            $coeff = $size[0] / $size[1];

            if (empty($height)) {
                $height = (int)($width / $coeff);
            } else {
                $newHeight = min($height, (int)($width / $coeff));

                if ($newHeight < $height) {
                    $height = $newHeight;
                } else {
                    $width = $height * $coeff;
                }
            }
        }

        $image->width = $width;
        $image->height = $height;
    }

    /**
     * Creates thumbnails of the images to improve loading speed and quality
     *
     * @param object $image
     * @param bool   $overwrite
     *
     * @since 3.4.0-FREE
     */
    private function createThumbnails(object $image, bool $overwrite)
    {
        if (!is_dir(JPATH_SITE . '/' . $image->folder . '/thumbs')) {
            mkdir(JPATH_SITE . '/' . $image->folder . '/thumbs', 0755);
            $fp = fopen(JPATH_SITE . '/' . $image->folder . '/thumbs/index.html', 'w');
            fclose($fp);
        }

        $filename = $image->folder . '/thumbs/' . $image->link;

        if (!file_exists($filename) || $overwrite) {
            $size = getimagesize(JPATH_BASE . '/' . $image->folder . '/' . $image->link);

            if ($size['mime'] == 'image/gif') {
                $imageOriginal = imagecreatefromgif($image->folder . '/' . $image->link);
                $imageThumbnail = imagecreatetruecolor($image->width, $image->height);
                imagecopyresampled($imageThumbnail, $imageOriginal, 0, 0, 0, 0, $image->width, $image->height, $size[0], $size[1]);
                imagegif($imageThumbnail, $image->folder . '/thumbs/' . $image->link);
            } elseif ($size['mime'] == 'image/jpeg') {
                $imageOriginal = imagecreatefromjpeg($image->folder . '/' . $image->link);
                $imageThumbnail = imagecreatetruecolor($image->width, $image->height);
                imagecopyresampled($imageThumbnail, $imageOriginal, 0, 0, 0, 0, $image->width, $image->height, $size[0], $size[1]);
                imagejpeg($imageThumbnail, $image->folder . '/thumbs/' . $image->link, 90);
            } elseif ($size['mime'] == 'image/png') {
                $imageOriginal = imagecreatefrompng($image->folder . '/' . $image->link);
                $imageThumbnail = imagecreatetruecolor($image->width, $image->height);
                imagecopyresampled($imageThumbnail, $imageOriginal, 0, 0, 0, 0, $image->width, $image->height, $size[0], $size[1]);
                imagepng($imageThumbnail, $image->folder . '/thumbs/' . $image->link);
            }

            if (!empty($imageOriginal) && is_resource($imageOriginal)) {
                imagedestroy($imageOriginal);
            }

            if (!empty($imageThumbnail) && is_resource($imageThumbnail)) {
                imagedestroy($imageThumbnail);
            }
        }
    }

    /**
     * Gets the cleaned image folder
     *
     * @param object $params
     *
     * @return string
     * @since 3.4.0-FREE
     */
    public function getFolder(object $params): string
    {
        $folder = htmlspecialchars($params->get('folder', 'images'));

        if (StringHelper::strpos($folder, Uri::base()) === 0) {
            $folder = str_replace(Uri::base(), '', $folder);
        }

        if (StringHelper::strpos($folder, JPATH_SITE) === 0) {
            $folder = str_replace(JPATH_BASE, '', $folder);
        }

        return str_replace('\\', '/', $folder);
    }

    /**
     * Gets images from the specified folder, if specified with all subfolders
     *
     * @param object $params
     * @param string $folder
     * @param bool   $subFolder
     *
     * @return array
     * @since 3.4.0-FREE
     */
    function getImages(object $params, string $folder, bool $subFolder = false): array
    {
        if ($subFolder) {
            return $this->getImagesSubfolder($params, $folder);
        }

        $images = [];
        $dir = JPATH_BASE . '/' . $folder;

        if (is_dir($dir)) {
            $files = [];

            if ($handle = opendir($dir)) {
                while (($file = readdir($handle)) !== false) {
                    if ($file != '.' && $file != '..' && $file != 'index.html') {
                        $files[] = $file;
                    }
                }
            }

            closedir($handle);

            $i = 0;
            $types = array_map('trim', explode(',', $params->get('type', 'jpg,gif,png')));

            foreach ($files as $img) {
                if (!is_dir($dir . '/' . $img)) {
                    foreach ($types as $type) {
                        if (preg_match('@' . $type . '$@i', $img)) {
                            $images[$i] = new \stdClass();
                            $images[$i]->link = $img;
                            $images[$i]->folder = str_replace('\\', '/', $folder);
                            $i++;
                            break;
                        }
                    }
                }
            }
        }

        return $images;
    }

    /**
     * Gets images from the specified folder and all subfolders
     *
     * @param object $params
     * @param string $folder
     *
     * @return array
     * @since 3.4.0-FREE
     */
    public function getImagesSubfolder(object $params, string $folder): array
    {
        $images = [];

        if (is_dir($folder)) {
            $handle = opendir($folder);

            if ($handle) {
                $types = array_map('trim', explode(',', $params->get('type', 'jpg,gif,png')));

                while (false !== ($file = readdir($handle))) {
                    if ($file != '.' && $file != '..' && $file != 'thumbs' && $file != 'index.html') {
                        $name = $folder . '/' . $file;

                        if (is_file($name)) {
                            if (in_array(strtolower(pathinfo($name, PATHINFO_EXTENSION)), $types, true)) {
                                $imageObject = new \stdClass();
                                $imageObject->link = pathinfo($name, PATHINFO_BASENAME);
                                $imageObject->folder = str_replace('\\', '/', pathinfo($name, PATHINFO_DIRNAME));

                                $images[] = $imageObject;
                            }
                        } elseif (is_dir($name)) {
                            $imagesSubfolder = $this->getImagesSubfolder($params, $name);
                            $images = array_merge($images, $imagesSubfolder);
                        }
                    }
                }
            }

            closedir($handle);
        }

        return $images;
    }

    /**
     * Loads needed JS and CSS files and instructions to the head section of the page
     *
     * @param string  $lightboxShow
     * @param integer $type
     * @param mixed   $width
     * @param mixed   $height
     * @param bool    $imageRotatorDuration
     *
     * @since 3.4.0-FREE
     */
    public function loadHeadData($lightboxShow, $type, $width = false, $height = false, $imageRotatorDuration = false)
    {
        $document = Factory::getDocument();

        if ($type == 1) {
            $css = '.random_image_extended {text-align: center; margin: 10px 0;}' . "\n";
            $css .= '.random_image_extended_single {display: inline-block; padding: 4px;}' . "\n";
            $css .= '.random_image_extended_small {text-align: right; font-size: 0.85em; margin-top: 15px;}' . "\n";
            $document->addStyleDeclaration($css);
        } elseif ($type == 2) {
            $head = [];

            if ($lightboxShow == 'slimbox') {
                HTMLHelper::_('behavior.framework');

                $head[] = '<link rel="stylesheet" href="modules/mod_random_image_extended/src/slimbox/css/slimbox.css" type="text/css" media="screen" />';
                $head[] = '<script type="text/javascript" src="modules/mod_random_image_extended/src/slimbox/js/slimbox.js"></script>';
            } elseif ($lightboxShow == 'milkbox') {
                HTMLHelper::_('behavior.framework', 'more');

                $head[] = '<link rel="stylesheet" href="modules/mod_random_image_extended/src/milkbox/milkbox.css" type="text/css" media="screen" />';
                $head[] = '<script type="text/javascript" src="modules/mod_random_image_extended/src/milkbox/milkbox.js"></script>';
            } elseif ($lightboxShow == 'shadowbox') {
                $head[] = '<link rel="stylesheet" href="modules/mod_random_image_extended/src/shadowbox/shadowbox.css" type="text/css" media="screen" />';
                $head[] = '<script type="text/javascript" src="modules/mod_random_image_extended/src/shadowbox/shadowbox.js"></script>';
                $head[] = '<script type="text/javascript">Shadowbox.init();</script>';
            } elseif ($lightboxShow == 'venobox') {
                HTMLHelper::_('jquery.framework');

                $head[] = '<script type="text/javascript" src="modules/mod_random_image_extended/src/venobox/venobox.js"></script>';
                $head[] = '<script type="text/javascript">jQuery(document).ready(function(){jQuery(\'.venobox\').venobox();});</script>';
                $head[] = '<link rel="stylesheet" href="modules/mod_random_image_extended/src/venobox/venobox.css" type="text/css" media="screen" />';
            }

            $head = "\n" . implode("\n", $head) . "\n";
            $document->addCustomTag($head);
        } elseif ($type == 3) {
            $css = '#slideshow-container  {width: ' . $width . 'px; height: ' . $height . 'px; position: relative; overflow: hidden; text-align: left; margin: auto;}' . "\n";
            $css .= '#slideshow-container img {display: inline-block; position: absolute; top: 0; left: 0; z-index: 1; opacity: 0.0}';
            $css .= '#slideshow-container img:first-child {opacity: 1.0}';
            $css .= '#slideshow-container img.active {z-index: 3;}';
            $css .= '#slideshow-container img.last-active {z-index: 2;}';
            $document->addStyleDeclaration($css);

            HTMLHelper::_('jquery.framework');

            // Inspired by http://jonraasch.com/blog/a-simple-jquery-slideshow
            $imageRotator = 'function slideSwitch() {
							    var $all = jQuery(\'#slideshow-container img\');
							    var $active = jQuery(\'#slideshow-container img.active\');
							    if ( $active.length == 0 ) $active = jQuery(\'#slideshow-container img:last\');
							    var $next =  $active.next().length ? $active.next()
										: jQuery(\'#slideshow-container img:first\');
							    $all.css({opacity: 0.0});
								$active.css({opacity: 1.0})
							        .addClass(\'last-active\')
									.animate({opacity: 0.0}, 1000);
							    $next.css({opacity: 0.0})
							        .addClass(\'active\')
									.animate({opacity: 1.0}, 1000, function() {
										$active.removeClass(\'active last-active\');
									});
							}

							jQuery(document).ready(function(){
								setInterval( "slideSwitch()", ' . $imageRotatorDuration . '000 );
							});';

            $document->addScriptDeclaration($imageRotator, 'text/javascript');
        }
    }

    /**
     * Loads information from a text file which has to be uploaded in the image folder with the name info.txt
     *
     * The entries in the info.txt have this structure:
     *
     * IMAGENAME.EXT|TITLE|LINK
     *
     * Each line one entry!
     *
     * @param string $folder
     *
     * @return array
     * @since 3.4.0-FREE
     */
    public function getFileInfo(string $folder): array
    {
        $linksInfo = [];
        $linksTextFile = $folder . '/info.txt';

        if (file_exists($linksTextFile)) {
            $linksFile = file($linksTextFile);
            $count = 0;

            foreach ($linksFile as $value) {
                $linksLine = explode('|', $value);

                if (empty($linksLine[2])) {
                    $linksLine[2] = '';
                }

                $linksInfo[$linksLine[0]] = [htmlspecialchars($linksLine[1]), htmlspecialchars($linksLine[2])];
                $count++;
            }
        }

        return $linksInfo;
    }

    /**
     * Creates the correct lightbox attribute for the template file
     *
     * @param string $lightboxShow
     *
     * @return string
     * @since 3.4.0-FREE
     */
    public function lightboxAttribute(string $lightboxShow): string
    {
        if ($lightboxShow == 'slimbox') {
            return 'rel="lightbox.random"';
        } elseif ($lightboxShow == 'milkbox') {
            return 'data-milkbox="milkbox"';
        } elseif ($lightboxShow == 'shadowbox') {
            return 'rel="shadowbox[random]"';
        } elseif ($lightboxShow == 'venobox') {
            return 'class="venobox" data-gall="venobox"';
        }

        return '';
    }
}
