<template>
	<div class="context-menu" v-show="menuOpen" ref="contextMenu" :style="{ top: yPos, left: xPos }">
		<button
			class="menu-option"
			v-for="option of sortedOptions"
			:key="option.label"
			v-show="option.show"
			@click.prevent="option.action"
		>
			<span class="menu-option__text">{{ option.label }}</span>
		</button>
	</div>
</template>

<script>
import Downloads from '@/utils/downloads'
import downloadQualities from '@js/qualities'
import { generatePath } from '@/utils/utils'

export default {
	data() {
		return {
			menuOpen: false,
			xPos: 0,
			yPos: 0,
			deezerHref: '',
			generalHref: '',
			imgSrc: ''
		}
	},
	computed: {
		options() {
			// In the action property:
			// Use arrow functions to keep the Vue instance in 'this'
			// Use normal functions to keep the object instance in 'this'
			const options = {
				cut: {
					label: this.$t('globals.cut'),
					show: true,
					position: 1,
					action: () => {
						document.execCommand('Cut')
					}
				},
				copy: {
					label: this.$t('globals.copy'),
					show: true,
					position: 2,
					action: () => {
						document.execCommand('Copy')
					}
				},
				copyLink: {
					label: this.$t('globals.copyLink'),
					show: false,
					position: 3,
					action: () => {
						navigator.clipboard.writeText(this.generalHref).catch(err => {
							console.error('Link copying failed', err)
						})
					}
				},
				copyImageLink: {
					label: this.$t('globals.copyImageLink'),
					show: false,
					position: 4,
					action: () => {
						navigator.clipboard.writeText(this.imgSrc).catch(err => {
							console.error('Image copying failed', err)
						})
					}
				},
				copyDeezerLink: {
					label: this.$t('globals.copyDeezerLink'),
					show: false,
					position: 5,
					action: () => {
						navigator.clipboard.writeText(this.generalHref).catch(err => {
							console.error('Deezer link copying failed', err)
						})
					}
				},
				paste: {
					label: this.$t('globals.paste'),
					show: true,
					position: 6,
					action: () => {
						navigator.clipboard.readText().then(text => {
							document.execCommand('insertText', undefined, text)
						})
					}
				}
			}

			let nextValuePosition = Object.values(options).length + 1

			downloadQualities.forEach((quality, index) => {
				options[quality.objName] = {
					label: `${this.$t('globals.download', [quality.label])}`,
					show: false,
					position: nextValuePosition + index,
					action: this.tryToDownloadTrack.bind(null, quality.value)
				}
			})

			return options
		},
		// This computed property is used for rendering the options in the wanted order
		// while keeping the options computed property an Object to make the properties
		// accessible via property name (es this.options.copyLink)
		sortedOptions() {
			return Object.values(this.options).sort((first, second) => {
				return first.position < second.position ? -1 : 1
			})
		}
	},
	mounted() {
		document.body.addEventListener('contextmenu', this.showMenu)
		document.body.addEventListener('click', this.hideMenu)
	},
	methods: {
		showMenu(contextMenuEvent) {
			contextMenuEvent.preventDefault()

			const { pageX, pageY, target: elementClicked } = contextMenuEvent

			const path = generatePath(elementClicked)

			this.positionMenu(pageX, pageY)

			// Show 'Copy Link' option
			if (elementClicked.matches('a')) {
				this.generalHref = elementClicked.href
				this.options.copyLink.show = true
			}

			// Show 'Copy Image Link' option
			if (elementClicked.matches('img')) {
				this.imgSrc = elementClicked.src
				this.options.copyImageLink.show = true
			}

			let deezerLink = null

			for (let i = 0; i < path.length; i++) {
				if (path[i] == document) break

				if (path[i].matches('[data-link]')) {
					deezerLink = path[i].dataset.link
					break
				}
			}

			// Show 'Copy Deezer Link' option
			if (deezerLink) {
				this.deezerHref = deezerLink
				this.showDeezerOptions()
			}

			this.menuOpen = true
		},
		hideMenu() {
			if (!this.menuOpen) return

			// Finish all operations before closing (may be not necessary)
			this.$nextTick()
				.then(() => {
					this.menuOpen = false

					this.options.copyLink.show = false
					this.options.copyDeezerLink.show = false
					this.options.copyImageLink.show = false

					downloadQualities.forEach(quality => {
						this.options[quality.objName].show = false
					})
				})
				.catch(err => {
					console.error(err)
				})
		},
		positionMenu(newX, newY) {
			this.xPos = `${newX}px`
			this.yPos = `${newY}px`

			this.$nextTick().then(() => {
				const { innerHeight, innerWidth } = window
				const menuXOffest = newX + this.$refs.contextMenu.getBoundingClientRect().width
				const menuYOffest = newY + this.$refs.contextMenu.getBoundingClientRect().height

				if (menuXOffest > innerWidth) {
					const difference = menuXOffest - innerWidth + 15
					this.xPos = `${newX - difference}px`
				}

				if (menuYOffest > innerHeight) {
					const difference = menuYOffest - innerHeight + 15
					this.yPos = `${newY - difference}px`
				}
			})
		},
		showDeezerOptions() {
			this.options.copyDeezerLink.show = true

			downloadQualities.forEach(quality => {
				this.options[quality.objName].show = true
			})
		},
		tryToDownloadTrack(qualityValue) {
			Downloads.sendAddToQueue(this.deezerHref, qualityValue)
		}
	}
}
</script>

<style lang="scss" scoped>
.context-menu {
	position: absolute;
	top: 0;
	left: 0;
	min-width: 100px;
	border-radius: 7px;
	background: var(--foreground-inverted);
	box-shadow: 4px 10px 18px 0px hsla(0, 0%, 0%, 0.15);
	overflow: hidden;
	z-index: 10000;
}

.menu-option {
	display: flex;
	align-items: center;
	width: 100%;
	height: 40px;
	padding-left: 10px;
	padding-right: 10px;
	color: var(--foreground);
	cursor: pointer;

	&:hover {
		background: var(--table-highlight);
		filter: brightness(150%);
	}

	&__text {
		text-transform: capitalize;
	}
}

// Resetting buttons only for this component (because the style is scoped)
button {
	color: var(--accent-text);
	color: unset;
	background-color: var(--accent-color);
	background-color: unset;
	min-width: unset;
	position: unset;
	border: unset;
	border-radius: unset;
	font-family: unset;
	font-weight: unset;
	font-size: unset;
	padding: unset;
	margin-right: unset;
	height: unset;
	text-transform: unset;
	cursor: unset;
	transition: unset;

	&:focus {
		outline: none;
	}

	&[disabled] {
		background-color: unset;
		color: unset;
		opacity: unset;
	}

	&.selective {
		background-color: unset;
		color: unset;

		&.active {
			background-color: unset;
			color: unset;
		}
	}

	&.with_icon {
		display: unset;
		align-items: unset;

		i {
			margin-left: unset;
		}
	}

	&:active {
		background-color: unset;
		transform: unset;
	}

	&:hover {
		background: unset;
		border: unset;
	}
}
</style>