<!--
 - @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
 -
 - @author John Molakvoæ <skjnldsv@protonmail.com>
 - @author Corentin Mors <medias@pixelswap.fr>
 -
 - @license AGPL-3.0-or-later
 -
 - This program is free software: you can redistribute it and/or modify
 - it under the terms of the GNU Affero 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 Affero General Public License for more details.
 -
 - You should have received a copy of the GNU Affero General Public License
 - along with this program. If not, see <http://www.gnu.org/licenses/>.
 -
 -->

<template>
	<!-- Errors handlers-->
	<EmptyContent v-if="error === 404" illustration-name="folder">
		{{ t('photos', 'This folder does not exist') }}
	</EmptyContent>
	<EmptyContent v-else-if="error">
		{{ t('photos', 'An error occurred') }}
	</EmptyContent>

	<!-- Folder content -->
	<div v-else-if="!loading">
		<Navigation
			v-if="isEmpty"
			key="navigation"
			:basename="path"
			:filename="'/'"
			:root-title="rootTitle" />

		<EmptyContent v-if="isEmpty" illustration-name="empty">
			{{ t('photos', 'No photos in here') }}
		</EmptyContent>

		<div class="grid-container">
			<VirtualGrid
				ref="virtualgrid"
				:items="contentList"
				:update-function="getContent"
				:get-column-count="() => gridConfig.count"
				:get-grid-gap="() => gridConfig.gap"
				:update-trigger-margin="700"
				:loader="loaderComponent" />
		</div>
	</div>
</template>

<script>
import moment from '@nextcloud/moment'
import { mapGetters } from 'vuex'

import getPhotos from '../services/PhotoSearch'

import EmptyContent from '../components/EmptyContent'
import File from '../components/File'
import SeparatorVirtualGrid from '../components/SeparatorVirtualGrid'
import VirtualGrid from 'vue-virtual-grid'
import Navigation from '../components/Navigation'
import Loader from '../components/Loader'

import cancelableRequest from '../utils/CancelableRequest'
import GridConfigMixin from '../mixins/GridConfig'
import { allMimes } from '../services/AllowedMimes'

export default {
	name: 'Timeline',
	components: {
		EmptyContent,
		VirtualGrid,
		Navigation,
	},
	mixins: [GridConfigMixin],
	props: {
		loading: {
			type: Boolean,
			required: true,
		},
		onlyFavorites: {
			type: Boolean,
			default: false,
		},
		mimesType: {
			type: Array,
			default: () => allMimes,
		},
		rootTitle: {
			type: String,
			required: true,
		},
		path: {
			type: String,
			default: '',
		},
	},

	data() {
		return {
			cancelRequest: null,
			done: false,
			error: null,
			page: 0,
			lastSection: '',
			loaderComponent: Loader,
		}
	},

	computed: {
		// global lists
		...mapGetters([
			'files',
			'timeline',
		]),
		// list of loaded medias
		fileList() {
			return this.timeline.map((fileId) => this.files[fileId])
		},
		// list of displayed content in the grid (titles + medias)
		contentList() {
			/** The goal of this flat map is to return an array of images separated by titles (months)
			 * ie: [{month1}, {image1}, {image2}, {month2}, {image3}, {image4}, {image5}]
			 * First we get the current month+year of the image
			 * We compare it to the previous image month+year
			 * If there is a difference we have to insert a title object before the current image
			 * If it's equal we just add the current image to the array
			 * Note: the injected param of objects are used to pass custom params to the grid lib
			 * In our case injected could be an image/video (aka file) or a title (year/month)
			 * Note2: titles are rendered full width and images are rendered on 1 column and 256x256 ratio
			 */
			return this.fileList.flatMap((file, index) => {
				const finalArray = []
				const currentSection = this.getFormatedDate(file.lastmod, 'YYYY MMMM')
				if (this.lastSection !== currentSection) {
					finalArray.push({
						id: `title-${index}`,
						injected: {
							year: this.getFormatedDate(file.lastmod, 'YYYY'),
							month: this.getFormatedDate(file.lastmod, 'MMMM'),
						},
						height: 90,
						columnSpan: 0, // means full width
						newRow: true,
						renderComponent: SeparatorVirtualGrid,
					})
					this.lastSection = currentSection // we keep track of the last section for the next batch
				}
				finalArray.push({
					id: `img-${file.fileid}`,
					injected: {
						...file,
						list: this.fileList,
						loadMore: this.getContent,
						canLoop: false,
					},
					width: 256,
					height: 256,
					columnSpan: 1,
					renderComponent: File,
				})
				return finalArray
			})
		},
		// is current folder empty?
		isEmpty() {
			return this.fileList.length === 0
		},
	},

	watch: {
		async onlyFavorites() {
			// reset component
			this.resetState()
			this.getContent()
		},
		async mimesType() {
			// reset component
			this.resetState()
			this.getContent()
		},
	},

	beforeMount() {
		this.getContent()
	},

	beforeDestroy() {
		// cancel any pending requests
		if (this.cancelRequest) {
			this.cancelRequest('Changed view')
		}
		this.resetState()
	},

	methods: {
		/** Return next batch of data depending on global offset
		 *
		 * @param {boolean} doReturn Returns a Promise with the list instead of a boolean
		 * @return {Promise<boolean>} Returns a Promise with a boolean that stops infinite loading
		 */
		async getContent(doReturn) {
			if (this.done) {
				return Promise.resolve(true)
			}

			// cancel any pending requests
			if (this.cancelRequest) {
				this.cancelRequest('Changed view')
			}

			// if we don't already have some cached data let's show a loader
			if (this.timeline.length === 0) {
				this.$emit('update:loading', true)
			}

			// done loading even with errors
			const { request, cancel } = cancelableRequest(getPhotos)
			this.cancelRequest = cancel

			const numberOfImagesPerBatch = this.gridConfig.count * 5 // loading 5 rows

			try {
				// Load next batch of images
				const files = await request(this.onlyFavorites, {
					page: this.page,
					perPage: numberOfImagesPerBatch,
					mimesType: this.mimesType,
				})

				// If we get less files than requested that means we got to the end
				if (files.length !== numberOfImagesPerBatch) {
					this.done = true
				}

				this.$store.dispatch('updateTimeline', files)
				this.$store.dispatch('appendFiles', files)

				this.page += 1

				if (doReturn) {
					return Promise.resolve(files)
				}

				return Promise.resolve(false)
			} catch (error) {
				if (error.response && error.response.status) {
					if (error.response.status === 404) {
						this.error = 404
						setTimeout(() => {
							this.$router.push({ name: this.$route.name })
						}, 3000)
					} else {
						this.error = error
					}
				}

				// cancelled request, moving on...
				console.error('Error fetching timeline', error)
				return Promise.resolve(true)
			} finally {
				// done loading even with errors
				this.$emit('update:loading', false)
				this.cancelRequest = null
			}
		},

		/**
		 * Reset this component data to a pristine state
		 */
		resetState() {
			this.$store.dispatch('resetTimeline')
			this.done = false
			this.error = null
			this.page = 0
			this.lastSection = ''
			this.$emit('update:loading', true)
			this.$refs.virtualgrid.resetGrid()
		},

		getFormatedDate(string, format) {
			return moment(string).format(format)
		},

	},

}
</script>

<style lang="scss" scoped>
@import '../mixins/GridSizes.scss';

.grid-container {
	@include grid-sizes using ($marginTop, $marginW) {
		padding: 0px #{$marginW}px 256px #{$marginW}px;
	}
}
</style>
