<?php
/**
 * Hyvä Themes - https://hyva.io
 * Copyright © Hyvä Themes 2020-present. All rights reserved.
 * This product is licensed per Magento install
 * See https://hyva.io/license
 */

declare(strict_types=1);

use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\StoreConfig;
use Hyva\Theme\ViewModel\HeroiconsOutline;
use Hyva\Theme\ViewModel\HeroiconsSolid;
use Magento\Catalog\Block\Product\View\Gallery;
use Magento\Catalog\Helper\Image;
use Magento\Framework\Escaper;

/** @var Escaper $escaper */
/** @var Gallery $block */
/** @var ViewModelRegistry $viewModels */

/** @var StoreConfig $storeConfig */
$storeConfig = $viewModels->require(StoreConfig::class);

/** @var HeroiconsOutline $heroicons */
$heroicons = $viewModels->require(HeroiconsOutline::class);

/** @var HeroiconsSolid $heroiconsSolid */
$heroiconsSolid = $viewModels->require(HeroiconsSolid::class);

$images = $block->getGalleryImages()->getItems();
$mainImage = current(array_filter($images, [$block, 'isMainImage']));

if (!empty($images) && empty($mainImage)) {
    $mainImage = reset($images);
}

/** @var Image $helper */
$helper = $block->getData('imageHelper');
$mainImageData = $mainImage ?
    $mainImage->getData('medium_image_url') :
    $helper->getDefaultPlaceholderUrl('image');

$smallWidth = $block->getImageAttribute('product_page_image_small', 'width', 90);
$smallHeight = $block->getImageAttribute('product_page_image_small', 'height', 90);
$mediumWidth = $block->getImageAttribute('product_page_image_medium', 'width', 700);
$mediumHeight = $block->getImageAttribute('product_page_image_medium', 'height', 700);

$galleryOptionShowRelated = $storeConfig->getStoreConfig('catalog/product_video/show_related') ?? false;
$galleryOptionVideoLoop = $storeConfig->getStoreConfig('catalog/product_video/video_auto_restart') ?? false;

$productName = $block->getProduct()->getName();
?>

<div id="gallery"
     x-data="initGallery()"
     x-bind="eventListeners"
     :class="{ 'fixed inset-0 z-50 bg-white p-4 overflow-auto': fullscreen }"
     class="w-full pt-6 md:pt-0 md:h-auto md:row-start-1 md:row-span-2 md:col-start-1"
>
    <div
        class="flex w-full flex-row-reverse gap-x-4 bg-white"
        :role="fullscreen ? 'dialog' : false"
        :aria-modal="fullscreen"
        :aria-label="fullscreen ? '<?= $escaper->escapeJs(__('Gallery modal fullscreen')) ?>' : false"
    >

        <!--child-->
        <div class="relative self-center w-full"
             :class="{ 'h-screen': fullscreen }"
             @touchstart.passive="handleTouchStart"
             @touchmove.passive="handleTouchMove"
             x-transition:enter="ease-out duration-500"
             x-transition:enter-start="opacity-0"
             x-transition:enter-end="opacity-100"
        >
            <div class="relative mb-6" aria-live="polite" aria-atomic="true">
                <?php
                /**
                 * The first image is a placeholder that determines the
                 * aspect ratio for the gallery. It will be hidden as
                 * soon as JS is loaded, but will keep reserving the
                 * necessary space in the layout for the other (absolute positioned)
                 * images. Hence, `invisible` instead of `x-show` or `hidden`
                 */
                ?>
                <img
                    alt="<?= $mainImage ? $escaper->escapeHtmlAttr($mainImage->getData('label')) : '' ?>"
                    title="<?= $mainImage ? $escaper->escapeHtmlAttr($mainImage->getData('label')) : '' ?>"
                    class="object-contain object-center w-full h-auto"
                    :class="fullscreen ? 'invisible max-h-screen-75' : 'invisible'"
                    src="<?= /* @noEscape */ $mainImageData ?>"
                    width="<?= /* @noEscape */ $mediumWidth ?>"
                    height="<?= /* @noEscape */ $mediumHeight ?>"
                    itemprop="image"
                />
                <template x-for="(image, index) in images" :key="index">
                    <img
                        :alt="image.caption || '<?= $escaper->escapeJs($productName) ?>'"
                        :title="image.caption || '<?= $escaper->escapeJs($productName) ?>'"
                        class="absolute top-0 inset-0 object-contain object-center w-full m-auto"
                        :class="{ 'max-h-screen-75': fullscreen }"
                        width="<?= /* @noEscape */ $mediumWidth ?>"
                        height="<?= /* @noEscape */ $mediumHeight ?>"
                        :loading="active !== index ? 'lazy' : 'eager'"
                        :src="fullscreen ? image.full : image.img"
                        x-transition.opacity.duration.500ms
                        x-show="active === index"
                    />
                </template>
                <button
                    type="button"
                    class="absolute inset-0 w-full outline-offset-2"
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Click to view image in fullscreen')) ?>"
                    x-ref="galleryFullscreenBtn"
                    x-show="!fullscreen && images[active].type !== 'video'"
                    x-cloak
                    @click="openFullscreen()"
                    @keydown.enter="openFullscreen()"
                ></button>
                <button
                    type="button"
                    class="group absolute inset-0 outline-offset-2 grid place-items-center"
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Play video')) ?>"
                    x-show="images[active].type === 'video' && !activeVideoType"
                    x-cloak
                    @click="activateVideo()"
                    @keydown.enter="activateVideo()"
                >
                    <?= $heroiconsSolid->playHtml(
                        'stroke-white/75 fill-black/20 transition ease-in group-hover:scale-110 md:w-24 md:h-24',
                        44,
                        44,
                        ['aria-hidden' => 'true']
                    ); ?>
                </button>
                <div class="absolute inset-0 hidden w-full h-full bg-white nonmobile"
                     :class="{ 'hidden': activeVideoType !== 'youtube' }"
                     x-transition.opacity.duration.500ms
                     x-show="images[active].type === 'video' && activeVideoType === 'youtube'"
                >
                    <div id="youtube-player" class="w-full h-full"></div>
                </div>
                <div class="absolute inset-0 hidden w-full h-full bg-white"
                     :class="{ 'hidden': activeVideoType !== 'vimeo' }"
                     x-transition.opacity.duration.500ms
                     x-show="images[active].type === 'video' && activeVideoType === 'vimeo'"
                >
                    <div id="vimeo-player" class="w-full h-full"></div>
                </div>
            </div>
        </div>

        <!--multiple image-->
        <div @resize.window.debounce="calcPageSize(); $nextTick(() => calcActive())">
            <div
                id="thumbs"
                class="flex items-center flex-col mt-4"
                :class="{ 'fixed justify-center bottom-0 left-0 right-0 mx-6': fullscreen }"
                style="min-height: 100px;"
                x-show="images.length > 1"
                x-cloak
            >
                <button
                    type="button"
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Previous')) ?>"
                    tabindex="-1"
                    class="hidden p-2.5 flex items-center justify-center self-stretch text-black outline-none focus:outline-none flex-none"
                    @click="scrollPrevious"
                ><?= $heroicons->chevronLeftHtml('', 24, 24, ['aria-hidden' => true]) ?></button>
                <div class="js_thumbs_slides thumbs-wrapper relative overflow-auto"
                     :class="{
         'flex-col w-[100px] max-h-[500px] overflow-y-auto': !fullscreen || window.innerWidth < 1024,
         'flex-row w-full  overflow-x-auto': fullscreen && window.innerWidth >= 1024
     }"
                     x-ref="jsThumbSlides"
                     @scroll.debounce="calcPageSize(); calcActive(); calcScrollStartEnd()"
                >

                <template x-for="(image, index) in images" :key="index">
                        <div class="js_thumbs_slide flex shrink-0 mb-3 mr-0">
                            <button
                                type="button"
                                @click.prevent="setActive(index);"
                                class="relative block border border-gray-300 hover:border-primary focus:border-primary"
                                :class="{'border-primary': active === index}"
                            >
                                <span class="sr-only">
                                    <?= $escaper->escapeHtml('View larger image') ?>
                                </span>
                                <img
                                    :src="image.thumb"
                                    :alt="image.caption || '<?= $escaper->escapeJs(__("%1 thumbnail", $productName)) ?>'"
                                    :title="image.caption || '<?= $escaper->escapeJs(__("%1 thumbnail", $productName)) ?>'"
                                    width="<?= /* @noEscape */ $smallWidth ?>"
                                    height="<?= /* @noEscape */ $smallHeight ?>"
                                />
                                <span
                                    class="absolute inset-0 grid place-items-center"
                                    x-show="image.type === 'video'"
                                >
                                    <?= $heroicons->videoCameraHtml('stroke-white/75 fill-black/20', 44, 44, ['aria-hidden' => 'true']); ?>
                                </span>
                            </button>
                        </div>
                    </template>
                </div>
                <button
                    type="button"
                    x-show="images.length > 1"
                    x-cloak
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Next')) ?>"
                    tabindex="-1"
                    class="hidden p-2.5 flex items-center justify-center self-stretch text-black outline-none focus:outline-none flex-none"
                    @click="scrollNext"
                ><?= $heroicons->chevronRightHtml('', 24, 24, ['aria-hidden' => true]) ?></button>
            </div>
        </div>


        <div class="absolute top-0 right-0 pt-4 pr-4">
            <button @click="closeFullScreen()"
                    type="button"
                    class="hidden text-gray-500 p-3 hover:text-gray-600 focus:text-gray-600
                        transition ease-in-out duration-150"
                    :class="{ 'hidden': !fullscreen, 'block': fullscreen }"
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Close fullscreen')) ?>"
            >
                <?= $heroicons->xHtml('', 24, 24, ['aria-hidden' => 'true']) ?>
            </button>
        </div>
    </div>
</div>
<script>
    function initGallery () {
        let touchXDown, touchYDown;

        return {
            "active": 0,
            "videoData": {},
            "activeVideoType": false,
            "autoplayVideo": false,
            "loopVideo": <?= $galleryOptionVideoLoop ? 'true' : 'false' ?>,
            "relatedVideos": <?= $galleryOptionShowRelated ? 'true' : 'false' ?>,
            "vimeoPlayer": null,
            "fullscreen": false,
            "isSlider": false,
            "initialImages": <?= /* @noEscape */ $block->getGalleryImagesJson() ?>,
            "images": <?= /* @noEscape */ $block->getGalleryImagesJson() ?>,
            "appendOnReceiveImages": <?=
                $block->getVar('gallery_switch_strategy', 'Magento_ConfigurableProduct') === 'append' ? 'true' : 'false'
                ?>,
            "activeSlide": 0,
            "isSliderStart": true,
            "isSliderEnd": false,
            "itemCount": 0,
            "pageSize": 4,
            "pageFillers": 0,
            "focusTrapListener": null,
            init() {
                this.initActive();
                this.$nextTick(() => {
                    this.calcPageSize();
                    this.calcScrollStartEnd();
                });

                this.$watch('fullscreen', open => {
                    this.$nextTick(() => {
                        this.scrollLock(open);

                        window.requestAnimationFrame(() => {
                            this.calcPageSize()
                        });
                    });
                })
            },
            receiveImages(images) {
                if (this.appendOnReceiveImages) {
                    const initialUrls = this.initialImages.map(image => image.full);
                    const newImages = images.filter(image => ! initialUrls.includes(image.full));
                    this.images = [].concat(this.initialImages, newImages);
                    this.setActive(newImages.length ? this.initialImages.length : 0);
                } else {
                    this.images = images;
                    this.setActive(0);
                }

                this.$nextTick(() => {
                    this.scrollTo(this.active);
                });

                this.itemCount = this.images.length;
            },
            resetGallery() {
                this.images = this.initialImages;
                this.itemCount = this.images.length;
                this.initActive();
                this.$nextTick(() => {
                    this.scrollTo(this.active);
                });
            },
            initActive() {
                let active = this.images.findIndex(function(image) {
                    return image.isMain === true
                });
                if (active === -1) {
                    active = 0;
                }
                this.setActive(active);
            },
            setActive(index) {
                this.active = index;
                this.activeVideoType = false;
                if (window.youtubePlayer) {
                    window.youtubePlayer.stopVideo();
                }
                if (this.vimeoPlayer) {
                    this.vimeoPlayer.contentWindow.postMessage(JSON.stringify({"method": "pause"}), "*");
                }
                if (this.images[index].type === 'video' && this.autoplayVideo) {
                    this.activateVideo();
                }
            },
            activateVideo() {
                const videoData = this.getVideoData();

                if (!videoData) { return }

                this.activeVideoType = videoData.type;

                if (videoData.type === "youtube") {
                    if (!window.youtubePlayer) {
                        this.initYoutubeAPI(videoData);
                    } else {
                        window.youtubePlayer.loadVideoById(videoData.id);
                    }

                } else if (videoData.type === "vimeo") {
                    this.initVimeoVideo(videoData);
                }
            },
            getVideoData() {
                const videoUrl = this.images[this.active] && this.images[this.active].videoUrl;

                if (!videoUrl) { return }

                let id,
                    type,
                    youtubeRegex,
                    vimeoRegex,
                    useYoutubeNoCookie = false;

                if (videoUrl.match(/youtube\.com|youtu\.be|youtube-nocookie.com/)) {
                    id = videoUrl.replace(/^\/(embed\/|v\/)?/, '').replace(/\/.*/, '');
                    type = 'youtube';

                    youtubeRegex = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/;
                    id = videoUrl.match(youtubeRegex)[1];

                    if (videoUrl.match(/youtube-nocookie.com/)) {
                        useYoutubeNoCookie = true;
                    }
                } else if (videoUrl.match(/vimeo\.com/)) {
                    type = 'vimeo';
                    vimeoRegex = new RegExp(['https?:\\/\\/(?:www\\.|player\\.)?vimeo.com\\/(?:channels\\/(?:\\w+\\/)',
                        '?|groups\\/([^\\/]*)\\/videos\\/|album\\/(\\d+)\\/video\\/|video\\/|)(\\d+)(?:$|\\/|\\?)'
                    ].join(''));
                    id = videoUrl.match(vimeoRegex)[3];
                }

                return id ? {
                    id: id, type: type, useYoutubeNoCookie: useYoutubeNoCookie
                } : false;
            },
            initYoutubeAPI(videoData) {
                if (document.getElementById('loadYoutubeAPI')) {
                    return;
                }
                const params = {
                    "autoplay": true
                };
                const loadYoutubeAPI = document.createElement('script');
                loadYoutubeAPI.src = 'https://www.youtube.com/iframe_api';
                loadYoutubeAPI.id = 'loadYoutubeAPI';
                const firstScriptTag = document.getElementsByTagName('script')[0];
                firstScriptTag.parentNode.insertBefore(loadYoutubeAPI, firstScriptTag);

                const host = (videoData.useYoutubeNoCookie) ?
                    'https://www.youtube-nocookie.com' :
                    'https://www.youtube.com';

                if (!this.relatedVideos) {
                    params.rel = 0;
                }
                const fireYoutubeAPI = document.createElement('script');
                fireYoutubeAPI.innerHTML = `function onYouTubeIframeAPIReady() {
                    window.youtubePlayer = new YT.Player('youtube-player', {
                        host: '${host}',
                        videoId: '${videoData.id}',
                        playerVars: ${JSON.stringify(params)},
                    });
                }`;
                firstScriptTag.parentNode.insertBefore(fireYoutubeAPI, firstScriptTag);
            },
            initVimeoVideo(videoData) {
                let additionalParams = '&autoplay=1';
                let src = '';

                const timestamp = new Date().getTime();
                const vimeoContainer = document.getElementById("vimeo-player");
                const videoId = videoData.id;

                if (!vimeoContainer || !videoId) return;

                if (this.loopVideo) {
                    additionalParams += '&loop=1';
                }
                src = 'https://player.vimeo.com/video/' +
                    videoId + '?api=1&player_id=vimeo' +
                    videoId +
                    timestamp +
                    additionalParams;
                vimeoContainer.innerHTML =
                    `<iframe id="${'vimeo' + videoId + timestamp}"
                        src="${src}"
                        width="640" height="360"
                        webkitallowfullscreen
                        mozallowfullscreen
                        allowfullscreen
                        referrerPolicy="origin"
                        allow="autoplay"
                        class="object-center w-full h-full object-fit"
                     ></iframe>`;

                this.vimeoPlayer = vimeoContainer.childNodes[0];
            },
            getSlider() {
                return this.$refs.jsThumbSlides;
            },
            calcPageSize() {
                const slider = this.getSlider();
                if (slider) {
                    const slideEl = slider.querySelector('.js_thumbs_slide'),
                        marginRight = parseInt(window.getComputedStyle(slideEl).marginRight);

                    this.itemCount = slider.querySelectorAll('.js_thumbs_slide').length;
                    this.pageSize = Math.round(slider.clientWidth / (slideEl.clientWidth + marginRight));
                    this.pageFillers = (
                        this.pageSize * Math.ceil(this.itemCount / this.pageSize)
                    ) - this.itemCount;

                    this.isSlider = ((slider.clientWidth - (this.itemCount * (slideEl.clientWidth + marginRight))) < 0);
                }
            },
            calcScrollStartEnd() {
                const slider = this.getSlider();
                if (slider) {
                    this.isSliderStart = slider.scrollLeft === 0;
                    this.isSliderEnd = Math.ceil(slider.scrollLeft + slider.offsetWidth) >= slider.scrollWidth;
                }
            },
            calcActive() {
                const slider = this.getSlider();
                if (slider) {
                    const sliderItems = this.itemCount + this.pageFillers;
                    const calculatedActiveSlide = slider.scrollLeft / (slider.scrollWidth / sliderItems);
                    this.activeSlide = Math.round(calculatedActiveSlide / this.pageSize) * this.pageSize;
                }
            },
            scrollPrevious() {
                this.scrollTo(this.activeSlide - this.pageSize);
            },
            scrollNext() {
                this.scrollTo(this.activeSlide + this.pageSize);
            },
            scrollTo(idx) {
                const slider = this.getSlider();
                if (slider) {
                    const slideWidth = slider.scrollWidth / (this.itemCount + this.pageFillers);
                    slider.scrollLeft = Math.floor(slideWidth) * idx;
                    this.activeSlide = idx;
                }
            },
            setActiveAndScrollTo(index) {
                this.setActive(index)
                if (this.isSlider) {
                    this.scrollTo(index);
                }
            },
            eventListeners: {
                ['@keydown.window.escape']() {
                    if (!this.fullscreen) return;
                    this.closeFullScreen()
                },
                ['@update-gallery.window'](event) {
                    this.receiveImages(event.detail);
                },
                ['@reset-gallery.window'](event) {
                    this.resetGallery();
                },
                ['@keyup.arrow-right.window']() {
                    if (!this.fullscreen) return;
                    this.nextItem();
                },
                ['@keyup.arrow-left.window']() {
                    if (!this.fullscreen) return;
                    this.previousItem();
                },
            },
            scrollLock(use = true) {
                document.body.style.overflow = use ? "hidden" : "";
            },
            openFullscreen() {
                this.fullscreen = true;


                hyva.trapFocus(this.$root);
            },
            closeFullScreen(setFocusTo = this.$refs.galleryFullscreenBtn) {
                this.fullscreen = false;
                hyva.releaseFocus(this.$root);
                this.$nextTick(() => {
                    this.calcPageSize();
                    setFocusTo && setFocusTo.focus()
                });
            },
            handleTouchStart(event) {
                if (
                    this.images.length <= 1 ||
                    event.touches.length > 1 ||
                    (window.visualViewport && window.visualViewport.scale > 1.01)
                ) return;
                const firstTouch = event.touches[0];

                touchXDown = firstTouch.clientX;
                touchYDown = firstTouch.clientY;
            },
            handleTouchMove(event) {
                if (
                    this.images.length <= 1 ||
                    event.touches.length > 1 ||
                    (window.visualViewport && window.visualViewport.scale > 1.01) ||
                    !touchXDown ||
                    !touchYDown
                ) return;

                const xDiff = touchXDown - event.touches[0].clientX;
                const yDiff = touchYDown - event.touches[0].clientY;

                if (Math.abs(xDiff) > Math.abs(yDiff)) {
                    const newIndex = xDiff > 0 ?  this.getNextIndex() : this.getPreviousIndex();
                    this.setActiveAndScrollTo(newIndex)
                }
                touchXDown = touchYDown = null;
            },
            getPreviousIndex() {
                return this.active > 0 ? this.active - 1 : this.itemCount - 1;
            },
            getNextIndex() {
                return this.active + 1 === this.itemCount ? 0 : this.active + 1;
            },
            previousItem() {
                if (this.active === 0) return;
                this.setActiveAndScrollTo(this.active - 1);
            },
            nextItem() {
                if ((this.active + 1) === this.itemCount) return;
                this.setActiveAndScrollTo(this.active + 1);
            },
        }
    }
</script>
