// /* eslint-disable func-names */
import {useState, useRef, useEffect} from 'react'
import '../editor.css'
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' // https://fontawesome.com/v5/docs/web/use-with/react
import {faVolumeMute, faVolumeUp, faPause, faPlay, faGripLinesVertical, faSync, faStepBackward, faStepForward, faCamera, faDownload, faEraser} from '@fortawesome/free-solid-svg-icons' // https://fontawesome.com/v5/docs/web/use-with/react

import {createFFmpeg, fetchFile} from '@ffmpeg/ffmpeg' // https://github.com/ffmpegwasm/ffmpeg.wasm/blob/master/docs/api.md


function Editor({videoUrl, timings, setTimings}) {

	//Boolean state to handle video mute
	const [isMuted, setIsMuted] = useState(false)

	//Boolean state to handle whether video is playing or not
	const [playing, setPlaying] = useState(false)

	//Float integer state to help with trimming duration logic
	const [difference, setDifference] = useState(0.2)

	//Boolean state to handle deleting grabber functionality
	const [deletingGrabber, setDeletingGrabber] = useState(false)

	//State for error handling
	const [currentWarning, setCurrentWarning] = useState(null)

	//State for imageUrl
	const [imageUrl, setImageUrl] = useState('')

	//Boolean state handling trimmed video
	const [trimmingDone, setTrimmingDone] = useState(false)

	//Integer state to blue progress bar as video plays
	const [seekerBar, setSeekerBar] = useState(0)


	//Ref handling metadata needed for trim markers
	const currentlyGrabbedRef = useRef({'index': 0, 'type': 'none'})

	//Ref handling the trimmed video element
	const trimmedVidRef = useRef()

	//Ref handling the initial video element for trimming
	const playVideoRef = useRef()

	//Ref handling the progress bar element
	const progressBarRef = useRef()

	//Ref handling the element of the current play time
	const playBackBarRef = useRef()

	//Variable for error handling on the delete grabber functionality
	const warnings = {'delete_grabber': (<div>Please click on the grabber (either start or end) to delete it</div>)}

	//State handling storing of the trimmed video
	const [trimmedVideo, setTrimmedVideo] = useState()

	//Integer state to handle the progress bars numerical incremation
	const [progress, setProgress] = useState(0)

	//Boolean state handling whether ffmpeg has loaded or not
	const [ready, setReady] = useState(false)

	//Ref to handle the current instance of ffmpeg when loaded
	const ffmpeg = useRef(null)


	//Function handling loading in ffmpeg
	const load = async () => {
		try{
			await ffmpeg.current.load()

			setReady(true)
		}
		catch(error) {
			console.log(error)
		}
	}

	//Loading in ffmpeg when this component renders
	useEffect(() => {
		ffmpeg.current = createFFmpeg({
			log: true,
			corePath: 'https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg-core.js'
		})
		load()
	// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [])


	//Lifecycle handling the logic needed for the progress bar - displays the blue bar that grows as the video plays
	// eslint-disable-next-line react-hooks/exhaustive-deps
	useEffect(() => {
		if(playVideoRef.current.onloadedmetadata) {
			const currentIndex = currentlyGrabbedRef.current.index
			const seek = (playVideoRef.current.currentTime - timings[0].start) / playVideoRef.current.duration * 100
			setSeekerBar(seek)
			progressBarRef.current.style.width = `${seekerBar}%`
			if((playVideoRef.current.currentTime >= timings[0].end)) {
				playVideoRef.current.pause()
				setPlaying(false)
				currentlyGrabbedRef.current = ({'index': currentIndex + 1, 'type': 'start'})
				progressBarRef.current.style.width = '0%'
				progressBarRef.current.style.left = `${timings[0].start / playVideoRef.current.duration * 100}%`
				playVideoRef.current.currentTime = timings[0].start
			}
		}

		window.addEventListener('keyup', (event) => {
			if(event.key === ' ') {
				playPause()
			}
		})

		//Handles the start and end metadata for the timings state
		const time = timings
		playVideoRef.current.onloadedmetadata = () => {
			if(time.length === 0) {
				time.push({'start': 0, 'end': playVideoRef.current.duration})
				setTimings(time)
				addActiveSegments()
			}
			else{
				addActiveSegments()
			}
		}
	})

	//Lifecycle that handles removing event listener from the mouse event on trimmer - Desktop browser
	useEffect(() => {
		return window.removeEventListener('mouseup', removeMouseMoveEventListener)
	// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [])

	//Lifecycle that handles removing event listener from the touch/pointer event on trimmer - mobile browser
	useEffect(() => {
		return window.removeEventListener('pointerup', removePointerMoveEventListener)
	// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [])

	//Function handling the trimmer movement logic
	const handleMouseMoveWhenGrabbed = (event) => {
		playVideoRef.current.pause()
		addActiveSegments()
		let playbackRect = playBackBarRef.current.getBoundingClientRect()
		let seekRatio = (event.clientX - playbackRect.left) / playbackRect.width
		const index = currentlyGrabbedRef.current.index
		const type = currentlyGrabbedRef.current.type
		let time = timings
		let seek = playVideoRef.current.duration * seekRatio
		if((type === 'start') && (seek > ((index !== 0) ? (time[index - 1].end + difference + 0.2) : 0)) && seek < time[index].end - difference){
			progressBarRef.current.style.left = `${seekRatio * 100}%`
			playVideoRef.current.currentTime = seek
			time[index]['start'] = seek
			setPlaying(false)
			setTimings(time)
		}
		else if((type === 'end') && (seek > time[index].start + difference) && (seek < (index !== (timings.length - 1) ? time[index].start - difference - 0.2 : playVideoRef.current.duration))){
			progressBarRef.current.style.left = `${time[index].start / playVideoRef.current.duration * 100}%`
			playVideoRef.current.currentTime = time[index].start
			time[index]['end'] = seek
			setPlaying(false)
			setTimings(time)
		}
		progressBarRef.current.style.width = '0%'
	}

	//Function that handles removing event listener from the mouse event on trimmer - Desktop browser
	const removeMouseMoveEventListener = () => {
		window.removeEventListener('mousemove', handleMouseMoveWhenGrabbed)
	}

	//Lifecycle that handles removing event listener from the mouse event on trimmer - Mobile browser
	const removePointerMoveEventListener = () => {
		window.removeEventListener('pointermove', handleMouseMoveWhenGrabbed)
	}

	//Function handling reset logic
	const reset = () => {
		playVideoRef.current.pause()

		setIsMuted(false)
		setPlaying(false)
		currentlyGrabbedRef.current = {'index': 0, 'type': 'none'}
		setDifference(0.2)
		setDeletingGrabber(false)
		setCurrentWarning(false)
		setImageUrl('')

		setTimings([{'start': 0, 'end': playVideoRef.current.duration}])
		playVideoRef.current.currentTime = timings[0].start
		progressBarRef.current.style.left = `${timings[0].start / playVideoRef.current.duration * 100}%`
		progressBarRef.current.style.width = '0%'
		addActiveSegments()
	}

	//Function handling thumbnail logic
	const captureSnapshot = () => {
		let video = playVideoRef.current
		const canvas = document.createElement('canvas')
		// scale the canvas accordingly
		canvas.width = video.videoWidth
		canvas.height = video.videoHeight
		// draw the video at that frame
		canvas.getContext('2d')
			.drawImage(video, 0, 0, canvas.width, canvas.height)
		// convert it to a usable data URL
		const dataURL = canvas.toDataURL()
		setImageUrl({imageUrl: dataURL})
	}

	//Function handling download of thumbnail logic
	const downloadSnapshot = () => {
		let a = document.createElement('a') //Create <a>
		a.href = imageUrl //Image Base64 Goes here
		a.download = 'Thumbnail.png' //File name Here
		a.click() //Downloaded file
	}

	//Function handling skip to previous logic
	const skipPrevious = () => {
		if(playing){
			playVideoRef.current.pause()
		}
		// let previousIndex = (currentlyGrabbed.index !== 0) ? (currentlyGrabbed.index - 1) : (timings.length - 1)
		// setCurrentlyGrabbed({currentlyGrabbed: {'index': previousIndex, 'type': 'start'}, playing: false})
		// currentlyGrabbedRef.current = {'index': previousIndex, 'type': 'start'}
		// progressBarRef.current.style.left = `${timings[previousIndex].start / playVideoRef.current.duration * 100}%`
		// progressBarRef.current.style.width = '0%'
		// playVideoRef.current.currentTime = timings[previousIndex].start
	}

	//Function handling play and pause logic
	const playPause = () => {
		if(playing){
			playVideoRef.current.pause()
		}
		else{
			if((playVideoRef.current.currentTime >= timings[0].end)) {
				playVideoRef.current.pause()
				setPlaying(false)
				currentlyGrabbedRef.current = {'index': 0, 'type': 'start'}
				playVideoRef.current.currentTime = timings[0].start
				progressBarRef.current.style.left = `${timings[0].start / playVideoRef.current.duration * 100}%`
				progressBarRef.current.style.width = '0%'
			}
			playVideoRef.current.play()
		}
		setPlaying(!playing)
	}

	//Function handling skip to next logic
	const skipNext = () => {
		if(playing){
			playVideoRef.current.pause()
		}
		// let nextIndex = (currentlyGrabbed.index !== (timings.length - 1)) ? (currentlyGrabbed.index + 1) : 0
		// setCurrentlyGrabbed({currentlyGrabbed: {'index': nextIndex, 'type': 'start'}, playing: false})
		// currentlyGrabbedRef.current = {'index': nextIndex, 'type': 'start'}
		// progressBarRef.current.style.left = `${timings[nextIndex].start / playVideoRef.current.duration * 100}%`
		// progressBarRef.current.style.width = '0%'
		// playVideoRef.current.currentTime = timings[nextIndex].start
	}

	//Function handling updating progress logic (clicking on progress bar to jump to different time durations)
	const updateProgress = (event) => {
		let playbackRect = playBackBarRef.current.getBoundingClientRect()
		let seekTime = ((event.clientX - playbackRect.left) / playbackRect.width) * playVideoRef.current.duration
		playVideoRef.current.pause()
		// find where seekTime is in the segment
		let index = -1
		let counter = 0
		for(let times of timings){
			if(seekTime >= times.start && seekTime <= times.end){
				index = counter
			}
			counter += 1
		}
		if(index === -1) {
			return
		}
		setPlaying(false)
		currentlyGrabbedRef.current = {'index': index, 'type': 'start'}
		progressBarRef.current.style.width = '0%' // Since the width is set later, this is necessary to hide weird UI
		progressBarRef.current.style.left = `${timings[index].start / playVideoRef.current.duration * 100}%`
		playVideoRef.current.currentTime = seekTime
	}

	//Function handling adding new trim markers logic
	const addGrabber = () => {
		const time = timings
		const end = time[time.length - 1].end + difference
		setDeletingGrabber({deletingGrabber: false, currentWarning: null})
		if(end >= playVideoRef.current.duration){
			return
		}
		time.push({'start': end + 0.2, 'end': playVideoRef.current.duration})
		setTimings(time)
		addActiveSegments()
	}

	//Function handling first step of deleting trimmer
	const preDeleteGrabber = () => {
		if(deletingGrabber){
			setDeletingGrabber({deletingGrabber: false, currentWarning: null})
		}
		else{
			setDeletingGrabber({deletingGrabber: true, currentWarning: 'delete_grabber'})
		}
	}

	//Function handling deletion of trimmers logic
	const deleteGrabber = (index) => {
		let time = timings
		setDeletingGrabber({deletingGrabber: false, currentWarning: null, currentlyGrabbed: {'index': 0, 'type': 'start'}})
		setDeletingGrabber({deletingGrabber: false, currentWarning: null, currentlyGrabbed: {'index': 0, 'type': 'start'}})
		if(time.length === 1){
			return
		}
		time.splice(index, 1)
		progressBarRef.current.style.left = `${time[0].start / playVideoRef.current.duration * 100}%`
		playVideoRef.current.currentTime = time[0].start
		progressBarRef.current.style.width = '0%'
		addActiveSegments()
	}

	//Function handling logic of time segments throughout videos duration
	const addActiveSegments = () => {
		let colors = ''
		let counter = 0
		colors += `, rgb(240, 240, 240) 0%, rgb(240, 240, 240) ${timings[0].start / playVideoRef.current.duration * 100}%`
		for(let times of timings) {
			if(counter > 0) {
				colors += `, rgb(240, 240, 240) ${timings[counter].end / playVideoRef.current.duration * 100}%, rgb(240, 240, 240) ${times.start / playVideoRef.current.duration * 100}%`
			}
			colors += `, #ccc ${times.start / playVideoRef.current.duration * 100}%, #ccc ${times.end / playVideoRef.current.duration * 100}%`
			counter += 1
		}
		colors += `, rgb(240, 240, 240) ${timings[counter - 1].end / playVideoRef.current.duration * 100}%, rgb(240, 240, 240) 100%`
		playBackBarRef.current.style.background = `linear-gradient(to right${colors})`
	}

	// Function handling logic for post trimmed video
	const saveVideo = async(fileInput) => {
		let metadata = {
			'trim_times': timings,
			'mute': isMuted
		}
		console.log(metadata.trim_times)
		const trimStart = metadata.trim_times[0].start
		const trimEnd = metadata.trim_times[0].end

		const trimmedVideo = trimEnd - trimStart

		console.log('Trimmed Duration: ', trimmedVideo)
		console.log('Trim End: ', trimEnd)

		try{
			//Disabling new-cap for FS function
			// eslint-disable-next-line new-cap
			ffmpeg.current.FS('writeFile', 'myFile.mp4', await fetchFile(videoUrl))

			ffmpeg.current.setProgress(({ratio}) => {
				console.log('ffmpeg progress: ', ratio)
				if(ratio < 0) {
					setProgress(0)
				}
				setProgress(Math.round(ratio * 100))
			})

			await ffmpeg.current.run('-ss', `${trimStart}`, '-accurate_seek', '-i', 'myFile.mp4', '-to', `${trimmedVideo}`, '-codec', 'copy', 'output.mp4')

			//Disabling new-cap for FS function
			// eslint-disable-next-line new-cap
			const data = ffmpeg.current.FS('readFile', 'output.mp4')

			const url = URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}))

			setTrimmedVideo(url)
			setTrimmingDone(true)
			// setLottiePlaying(false)
		}
		catch(error) {
			console.log(error)
		}
	}

	return (
		<div className='wrapper'>
			{/* Video element for the trimmed video */}
			{trimmingDone ?
				<div
					style={{
						maxHeight: '100vh',
						marginTop: '50vh'
					}}>
					<video
						style={{
							width: '100%',
							marginTop: '100px',
							borderRadius: '20px',
							border: '4px solid #0072cf'
						}}
						ref={trimmedVidRef}
						controls
						autoload='metadata'
						onClick={() => console.log(trimmedVidRef.current.duration)}
					>
						<source src={trimmedVideo} type='video/mp4' />
					</video>
				</div>
				: null
			}
			{/* Main video element for the video editor */}
			<video className='video'
				autoload='metadata'
				muted={isMuted}
				ref={playVideoRef}
				onLoadedData={() => {
					console.log(playVideoRef)
					playPause()
				}}
				onClick={() => {
					playPause()
				}}
				onTimeUpdate={() => {
					setSeekerBar(progressBarRef.current.style.width)
				}}
			>
				<source src={videoUrl} type='video/mp4' />
			</video>
			<div className='playback'>
				{/* If there is an instance of the playVideoRef, render the trimmer markers */}
				{playVideoRef.current ?
					Array.from(timings).map((timing, index) => (
						<div key={index}
						>
							<div key={'grabber_' + index}>
								{/* Markup and logic for the start trim marker */}
								<div id='grabberStart' className='grabber start'
									style={{left: `${timings[0].start / playVideoRef.current.duration * 100}%`}}
									// Events for desktop - Start marker
									onMouseDown={(event) => {
										if(deletingGrabber){
											deleteGrabber(index)
										}
										else{
											currentlyGrabbedRef.current = {'index': index, 'type': 'start'}
											window.addEventListener('mousemove', handleMouseMoveWhenGrabbed)
											window.addEventListener('mouseup', removeMouseMoveEventListener)
										}
									}}
									//Events for mobile - Start marker
									onPointerDown={() => {
										if(deletingGrabber){
											deleteGrabber(index)
										}
										else{
											currentlyGrabbedRef.current = {'index': index, 'type': 'start'}
											window.addEventListener('pointermove', handleMouseMoveWhenGrabbed)
											window.addEventListener('pointerup', removePointerMoveEventListener)
										}
									}}
								>
									<svg version='1.1' xmlns='http://www.w3.org/2000/svg' x='0' y='0' width='10' height='14' viewBox='0 0 10 14' xmlSpace='preserve'>
										<path className='st0' d='M1 14L1 14c-0.6 0-1-0.4-1-1V1c0-0.6 0.4-1 1-1h0c0.6 0 1 0.4 1 1v12C2 13.6 1.6 14 1 14zM5 14L5 14c-0.6 0-1-0.4-1-1V1c0-0.6 0.4-1 1-1h0c0.6 0 1 0.4 1 1v12C6 13.6 5.6 14 5 14zM9 14L9 14c-0.6 0-1-0.4-1-1V1c0-0.6 0.4-1 1-1h0c0.6 0 1 0.4 1 1v12C10 13.6 9.6 14 9 14z'/>
									</svg>
								</div>
								{/* Markup and logic for the end trim marker */}
								<div id='grabberEnd' className='grabber end'
									style={{left: `${timings[0].end / playVideoRef.current.duration * 100}%`}}
									//Events for desktop - End marker
									onMouseDown={(event) => {
										if(deletingGrabber){
											deleteGrabber(index)
										}
										else{
											currentlyGrabbedRef.current = {'index': index, 'type': 'end'}
											window.addEventListener('mousemove', handleMouseMoveWhenGrabbed)
											window.addEventListener('mouseup', removeMouseMoveEventListener)
										}
									}}
									//Events for mobile - End marker
									onPointerDown={() => {
										if(deletingGrabber){
											deleteGrabber(index)
										}
										else{
											currentlyGrabbedRef.current = {'index': index, 'type': 'end'}
											window.addEventListener('pointermove', handleMouseMoveWhenGrabbed)
											window.addEventListener('pointerup', removePointerMoveEventListener)
										}
									}}
								>
									<svg version='1.1' xmlns='http://www.w3.org/2000/svg' x='0' y='0' width='10' height='14' viewBox='0 0 10 14' xmlSpace='preserve'>
										<path className='st0' d='M1 14L1 14c-0.6 0-1-0.4-1-1V1c0-0.6 0.4-1 1-1h0c0.6 0 1 0.4 1 1v12C2 13.6 1.6 14 1 14zM5 14L5 14c-0.6 0-1-0.4-1-1V1c0-0.6 0.4-1 1-1h0c0.6 0 1 0.4 1 1v12C6 13.6 5.6 14 5 14zM9 14L9 14c-0.6 0-1-0.4-1-1V1c0-0.6 0.4-1 1-1h0c0.6 0 1 0.4 1 1v12C10 13.6 9.6 14 9 14z'/>
									</svg>
								</div>
							</div>
						</div>
					))
					: []}
				<div className='seekable' ref={playBackBarRef} onClick={updateProgress}></div>
				<div className='progress' ref={progressBarRef}></div>
			</div>

			<div className='controls'>
				<div className='player-controls'>
					<button className='settings-control' title='Reset Video' onClick={reset}><FontAwesomeIcon icon={faSync} /></button>
					<button className='settings-control' title='Mute/Unmute Video' onClick={() => setIsMuted({isMuted: !isMuted})}>{isMuted ? <FontAwesomeIcon icon={faVolumeMute} /> : <FontAwesomeIcon icon={faVolumeUp} />}</button>
					<button className='settings-control' title='Capture Thumbnail' onClick={captureSnapshot}><FontAwesomeIcon icon={faCamera} /></button>
				</div>
				<div className='player-controls'>
					<button className='seek-start' title='Skip to previous clip' onClick={skipPrevious}><FontAwesomeIcon icon={faStepBackward} /></button>
					<button className='play-control' title='Play/Pause' onClick={playPause} >{playing ? <FontAwesomeIcon icon={faPause} /> : <FontAwesomeIcon icon={faPlay} /> }</button>
					<button className='seek-end' title='Skip to next clip' onClick={skipNext}><FontAwesomeIcon icon={faStepForward} /></button>
				</div>
				<div>
					<button title='Add grabber' className='trim-control margined' onClick={addGrabber}>Add <FontAwesomeIcon icon={faGripLinesVertical} /></button>
					<button title='Delete grabber' className='trim-control margined' onClick={preDeleteGrabber}>Delete <FontAwesomeIcon icon={faGripLinesVertical} /></button>
					<button title='Save changes' className='trim-control' onClick={saveVideo}>Save</button>
				</div>
			</div>
			{ready ?
				<div></div>
				:
				<div>Loading...</div>
			}
			{currentWarning != null ? <div className={'warning'}>{warnings[currentWarning]}</div> : ''}
			{(imageUrl !== '') ?
				<div className={'marginVertical'}>
					<img src={imageUrl} className={'thumbnail'} alt='Photos' />
					<div className='controls'>
						<div className='player-controls'>
							<button className='settings-control' title='Reset Video' onClick={downloadSnapshot}><FontAwesomeIcon icon={faDownload} /></button>
							<button className='settings-control' title='Save Video' onClick={() => {
								setImageUrl('')
							}}><FontAwesomeIcon icon={faEraser} /></button>
						</div>
					</div>
				</div>
				: ''}
		</div>
	)
}

export default Editor