<script lang="ts">
    import { browser } from '$app/env'
    import { page } from '$app/stores'
    import { goto } from '$app/navigation'
    import { onMount, tick } from 'svelte'
    import { scale } from 'svelte/transition'
    import { quartOut } from 'svelte/easing'
    import { getAssetUrlKey } from '$utils/helpers'
    import { fetchAPI } from '$utils/api'
    import { previousPage } from '$utils/stores'
    import { throttle } from '$utils/functions'
    import { swipe } from '$utils/interactions/swipe'
    import dayjs from 'dayjs'
    import anime from 'animejs'
    import type { AnimeTimelineInstance } from 'animejs'
    // Components
    import Metas from '$components/Metas.svelte'
    import SplitText from '$components/SplitText.svelte'
    import PageTransition from '$components/PageTransition.svelte'
    import Image from '$components/atoms/Image.svelte'
    import Icon from '$components/atoms/Icon.svelte'
    import IconArrow from '$components/atoms/IconArrow.svelte'
    import ButtonCircle from '$components/atoms/ButtonCircle.svelte'

    export let photos: any[]
    export let location: any
    export let currentIndex: number
    export let countPhotos: number
    export let limit: number
    export let offset: number

    enum directions { PREV, NEXT }

    let innerWidth: number
    let fullscreenEl: HTMLElement
    let globalOffset = offset
    let isLoading = false
    let isFullscreen = false
    let hasNext = offset + limit < countPhotos
    let hasPrev = offset > 0

    // Define if we can navigate depending on loading state, existing photos and index being first or last
    $: canGoPrev = !isLoading && (hasNext || currentIndex !== photos.length - 1)
    $: canGoNext = !isLoading && (hasPrev || currentIndex !== 0)
    // Define current photo
    $: currentPhoto = photos[currentIndex]
    $: currentPhotoIndex = globalOffset + currentIndex + 1

    // Take 7 photos in the global photos array (5 for current, 1 before first and 1 after last)
    // Start one index before the current image since the first one will be invisible
    $: sliceStart = Math.max(currentIndex - 1, 0)
    $: visiblePhotos = photos.slice(sliceStart, sliceStart + 7)

    // Load previous photos
    $: if (currentIndex === 0 && hasPrev) {
        loadPhotos(photos[0].id)
    }
    // Load next photos
    $: if (currentIndex === photos.length - 5 && hasNext) {
        loadPhotos(photos[photos.length - 1].id, directions.NEXT)
    }

    // Change URL to current photo slug
    $: if (browser && currentPhoto) {
        window.history.replaceState(null, '', $page.url.pathname.replace($page.params.photo, currentPhoto.slug))
    }

    // Define previous URL
    $: previousUrl = $previousPage ?? `/${location.country.slug}/${location.slug}`


    /**
     * Photo navigation
     */
    // Go to next photo
    const goToNext = throttle(() => {
        canGoPrev && currentIndex++
    }, 200)

    // Go to previous photo
    const goToPrevious = throttle(() => {
        canGoNext && (currentIndex = Math.max(currentIndex - 1, 0))
    }, 200)

    // Close viewer and go to previous page
    const closeViewer = () => {
        goto(previousUrl, { replaceState: false, noscroll: true, keepfocus: true })
    }

    // Enable navigation with keyboard
    const handleKeydown = ({ key, defaultPrevented }: KeyboardEvent) => {
        if (defaultPrevented) return
        switch (key) {
            case 'ArrowLeft': goToPrevious(); break;
            case 'ArrowRight': goToNext(); break;
            case 'Escape': closeViewer(); break;
            default: return;
        }
    }

    // Enable swipe gestures
    const handleSwipe = ({ detail }: CustomEvent<string>) => {
        // Swipe up and down on mobile/small screens
        if (innerWidth < 992) {
            switch (detail) {
                case '-y': goToNext(); break;
                case 'y': goToPrevious(); break;
            }
        }
        // Swipe left and right on larger screens
        else {
            switch (detail) {
                case '-x': goToNext(); break;
                case 'x': goToPrevious(); break;
            }
        }
    }


    /**
     * Fullscreen for mobile
     */
    const toggleFullscreen = async () => {
        if (innerWidth < 992) {
            isFullscreen = !isFullscreen

            // Scroll at middle of photo
            if (isFullscreen) {
                // Wait for fullscreen children to be mounted
                await tick()
                const picture = fullscreenEl.querySelector('picture')
                const image = fullscreenEl.querySelector('img')
                picture.scrollTo((image.offsetWidth - innerWidth) / 2, 0)
            }
        }
    }


    /**
     * Load photos
     */
    const loadPhotos = async (id: number, direction: directions = directions.PREV) => {
        // Block loading new photos if already loading
        if (isLoading) return

        // Set state to loading
        isLoading = true

        // Load new prev or next photos
        const isPrev = direction === directions.PREV
        const res = await fetchAPI(`
            query {
                photos: photo (
                    filter: {
                        location: { slug: { _eq: "${location.slug}" }},
                        id: { _${isPrev ? 'gt' : 'lt'}: ${id} },
                        status: { _eq: "published" },
                    },
                    sort: "${isPrev ? '' : '-'}id",
                    limit: ${limit},
                ) {
                    id
                    title
                    slug
                    date_taken
                    image {
                        id
                        title
                    }
                    city
                }
            }
        `)
        const { data: { photos: newPhotos }} = res

        // Not loading anymore
        isLoading = false

        if (newPhotos) {
            // Direction: Previous
            if (direction === directions.PREV) {
                // Append new photos
                photos = [
                    ...newPhotos.reverse(), // Reverse array from isPrev sort
                    ...photos
                ]
                // Increment current index by new amount of photos
                currentIndex += newPhotos.length
                // Decrement global offset by new amount of photos
                globalOffset -= newPhotos.length

                // No more prev photos available
                if (newPhotos.length === 0) {
                    hasPrev = false
                }
            }
            // Direction: Next
            else {
                // Append new photos
                photos = [
                    ...photos,
                    ...newPhotos
                ]

                // No more next photos available
                if (newPhotos.length === 0) {
                    hasNext = false
                }
            }
        } else {
            throw new Error('Error while loading new photos')
        }
    }


    onMount(() => {
        /**
         * Animations
         */
        // Setup animations
        const timeline: AnimeTimelineInstance = anime.timeline({
            duration: 1600,
            easing: 'easeOutQuart',
        })

        anime.set('.viewer__picture', {
            opacity: 0,
        })
        anime.set('.viewer__picture.is-1', {
            translateY: 24,
        })

        // Photos
        timeline.add({
            targets: '.viewer__picture.is-1',
            opacity: 1,
            translateY: 0,
            duration: 900,
        }, 250)
        timeline.add({
            targets: '.viewer__picture:not(.is-0):not(.is-1)',
            opacity: 1,
            translateX (element: HTMLElement, index: number) {
                const x = getComputedStyle(element).getPropertyValue('--offset-x').trim()
                return [`-${x}`, 0]
            },
            delay: anime.stagger(55)
        }, 500)

        // Prev/Next buttons
        timeline.add({
            targets: '.viewer__controls button',
            translateX (item: HTMLElement) {
                let direction = item.classList.contains('prev') ? -1 : 1
                return [16 * direction, 0]
            },
            opacity: [0, 1],
        }, 450)


        // Infos
        timeline.add({
            targets: '.viewer__info > *',
            translateY: [24, 0],
            opacity: [0, 1],
            delay: anime.stagger(200)
        }, 400)


        anime.set('.viewer__index', {
            opacity: 0
        })
        // Index
        timeline.add({
            targets: '.viewer__index',
            opacity: 1,
            duration: 900,
        }, 600)
        // Fly each number
        timeline.add({
            targets: '.viewer__index .char',
            translateY: ['100%', 0],
            delay: anime.stagger(200),
            duration: 1000,
        }, 700)

        // Transition in
        requestAnimationFrame(timeline.play)
    })
</script>

<svelte:window
    bind:innerWidth
    on:keydown={handleKeydown}
/>

{#if currentPhoto}
<Metas
    title="{currentPhoto.title} - Houses Of {location.name}"
    description=""
    image={getAssetUrlKey(currentPhoto.image.id, 'share')}
/>
{/if}


<PageTransition name="viewer">
    <div class="container grid">
        <p class="viewer__notice text-label">Tap for fullscreen</p>

        <ButtonCircle
            tag="a"
            url={previousUrl}
            color="purple"
            class="viewer__close shadow-box-dark"
        >
            <svg width="12" height="12">
                <use xlink:href="#cross">
            </svg>
        </ButtonCircle>

        <div class="viewer__carousel">
            <div class="viewer__images" use:swipe on:swipe={handleSwipe} on:tap={toggleFullscreen}>
                {#each visiblePhotos as photo, index (photo.id)}
                    <div class="viewer__picture is-{currentIndex === 0 ? index + 1 : index}">
                        <Image
                            class="photo"
                            id={photo.image.id}
                            alt={photo.title}
                            sizeKey="photo-list"
                            sizes={{
                                small: { width: 500 },
                                medium: { width: 850 },
                                large: { width: 1280 },
                            }}
                            ratio={1.5}
                        />
                    </div>
                {/each}

                <div class="viewer__controls">
                    <ButtonCircle class="prev shadow-box-dark" disabled={!canGoNext} clone={true} on:click={goToPrevious}>
                        <IconArrow color="pink" flip={true} />
                    </ButtonCircle>
                    <ButtonCircle class="next shadow-box-dark" disabled={!canGoPrev} clone={true} on:click={goToNext}>
                        <IconArrow color="pink" />
                    </ButtonCircle>
                </div>


                <div class="viewer__index title-index">
                    <SplitText text="{(currentPhotoIndex < 10) ? '0' : ''}{currentPhotoIndex}" mode="chars" />
                </div>
            </div>

            <div class="viewer__info">
                <h1 class="title-medium">{currentPhoto.title}</h1>

                <div class="detail text-info">
                    <a href="/{location.country.slug}/{location.slug}" sveltekit:prefetch sveltekit:noscroll>
                        <Icon class="icon" icon="map-pin" label="Map pin" />
                        <span>
                            {#if currentPhoto.city}
                                {currentPhoto.city}, {location.name}, {location.country.name}
                            {:else}
                                {location.name}, {location.country.name}
                            {/if}
                        </span>
                    </a>
                    <span class="sep">&middot;</span> <time datetime={dayjs(currentPhoto.date_taken).format('YYYY-MM-DD')}>{dayjs(currentPhoto.date_taken).format('MMMM YYYY')}</time>
                </div>
            </div>
        </div>
    </div>

    {#if isFullscreen}
        <div class="viewer__fullscreen" bind:this={fullscreenEl} on:click={toggleFullscreen}>
            <div class="inner" transition:scale={{ easing: quartOut, start: 1.1, duration: 1000 }}>
                <Image
                    id={currentPhoto.image.id}
                    sizeKey="photo-grid-large"
                    width={1266}
                    height={844}
                    alt={currentPhoto.title}
                />
                <ButtonCircle color="gray-medium" class="close">
                    <svg width="18" height="18" viewBox="0 0 18 18" fill="#fff" xmlns="http://www.w3.org/2000/svg">
                        <path fill-rule="evenodd" clip-rule="evenodd" d="M7.751 0c4.274 0 7.752 3.477 7.752 7.751 0 1.846-.65 3.543-1.73 4.875l3.99 3.991a.81.81 0 1 1-1.146 1.146l-3.99-3.991a7.714 7.714 0 0 1-4.876 1.73C3.477 15.503 0 12.027 0 7.753 0 3.476 3.477 0 7.751 0Zm0 1.62a6.138 6.138 0 0 0-6.13 6.131 6.138 6.138 0 0 0 6.13 6.132 6.138 6.138 0 0 0 6.131-6.132c0-3.38-2.75-6.13-6.13-6.13Zm2.38 5.321a.81.81 0 1 1 0 1.62h-4.76a.81.81 0 1 1 0-1.62h4.76Z" />
                    </svg>
                </ButtonCircle>
            </div>
        </div>
    {/if}
</PageTransition>