/*
	Created by Michael Schuijff <michael@reglobe.nl>
	Copyright Lost Images, The Netherlands
	
	For more information, visit www.michaelschuijff.nl
*/

router.register('/comments', () => {
	router.redirect('/');
});

router.register(['/comments/$id', '/cpmments/$id/$comment'], (values) => {
	if (!config.user) {
		if (!config.bot) {
			router.redirect('/login', { url: router.getURL() });
		} else {
			router.noIndex();
		}

		return;
	}
	
	let url = '/comments/$id', urls = [url];
	
	if (values.comment) {
		url += '/' + values.comment;
	}
	
	if (~!urls.indexOf(url)) {
		urls[urls.length] = url;
	}
	
	let search = encodeURIData(Object.assign({}, values, { id: null, comment: null }));
	
	if (search) {
		url += '?' + search;
	}

	if (~!urls.indexOf(url)) {
		urls[urls.length] = url;
	}
	
	let view = createView({
		titleBar: __('Comments'), hideMainTitleBar: true, hideBottomMenu: true, buttons: ['close'], urls: urls, spinner: true, pullToRefresh: true
	});
	
	let req;
	
	function fetchComments () {
		req = api.get({
			url: '/comments/' + values.id,
			data: { comment_id: values.comment },
			success: (result) => {
				view.content.innerHTML = '';
				
				let comments = renderComments(result, result.post);
				
				view.content.append(comments);
				
				view.hideSpinner();
				view.revertPullToRefresh();
				
				if (values.comment) {
					let element = comments.querySelector('.comment-' + values.comment);
					
					if (element) {
						let rect = element.getBoundingClientRect();
						view.scrollTop += rect.top - (window.innerHeight - rect.height) / 2, view.updateScroll();
					}
					
					delete values.comment;
				}
			},
			complete: () => req = null
		});
	}
	
	
	view.register('load', () => {
		fetchComments();
	});
	
	view.register('pause', () => {
		let elements = view.content.querySelectorAll('.edit-comment');
		
		for (let element of elements) {
			element.remove();
		}
	});

	view.register('refresh', () => {
		if (req) {
			req.abort();
		}
		
		view.spinner();
		fetchComments();
	});
	
	view.register('refresh destroy', () => {
		let elements = view.content.querySelectorAll('[data-blob-url]');
		
		for (let element of elements) {
			URL.revokeObjectURL(element.getAttribute('data-blob-url'));
		}
	});
	
	return view;	
});

function renderComments (values, post) {
	let container = createElement('div');
	
	if ('comment_count' in values) {
		container.className = 'comments';	
	} else {
		container.className = 'replies';
	}
	
	let buttonPrev = createElement('button', 'button button-prev', __('Earlier comments'))
		, buttonMore = createElement('button', 'button button-more', __('More comments'));
	
	if (!values.id) {
		[buttonPrev.innerHTML, buttonMore.innerHTML] = [buttonMore.innerHTML, buttonPrev.innerHTML];
	}
	
	let firstComment, lastComment;
	
	let reqBefore;
	
	function fetchCommentsBefore () {
		let data = { parent_id: values.id };
		
		if (values.id) {
			data.until_id = firstComment;
		} else {
			data.since_id = firstComment;
		}
		
		reqBefore = api.get({
			url: '/comments/' + post.id,
			data: data,
			success: (result) => {
				let fragment = document.createDocumentFragment();
				
				for (let comment of result.comments) {
					fragment.append(renderComment(comment, post));
				}
				
				if (result.offset || result.more) {
					buttonPrev.after(fragment);
				} else {
					buttonPrev.replaceWith(fragment);
				}
				
				firstComment = result.comments[0].id;
			},
			complete: () => reqBefore = null
		});
	}
	
	let reqAfter;
	
	function fetchCommentsAfter () {
		let data = { parent_id: values.id };
		
		if (values.id) {
			data.since_id = lastComment;
		} else {
			data.until_id = lastComment;
		}
		
		reqAfter = api.get({
			url: '/comments/' + post.id,
			data: data,
			success: (result) => {
				let fragment = document.createDocumentFragment();
				
				for (let comment of result.comments) {
					fragment.append(renderComment(comment, post));
				}
				
				if (result.more) {
					buttonMore.before(fragment);
				} else {
					buttonMore.replaceWith(fragment);
				}
				
				lastComment = result.comments[result.comments.length - 1].id;
			},
			complete: () => reqAfter = null
		});
	}
	
	if (values.comment_count || values.reply_count) {
		let comments = values.comments || values.replies;
		
		if (values.offset) {
			buttonPrev.onclick = () => {
				reqBefore || fetchCommentsBefore();
			}
			
			container.append(buttonPrev);
		}
		
		firstComment = comments[0].id;
		
		lastComment = comments[comments.length - 1].id;
		
		for (let comment of comments) {
			container.append(renderComment(comment, post));
		}
		
		if (values.more) {
			buttonMore.onclick = () => {
				reqAfter || fetchCommentsAfter();
			}
			
			container.append(buttonMore);
		}
	}
	
	if (post && (post.comments == 'yes' || post.comments == 'followers')) {
		container.append(renderCommentForm(values, post));
	} else {
		container.append(createElement('div', 'no-content', __('You cannot comment on this post.')));
	}
	
	function renderComment (comment, post) {
		let container = createElement('div', 'comment comment-' + comment.id + ' user-' + comment.user.id);
		
		let user = renderUser(comment.user, [comment.like ? 'heart liked' : 'heart']);
		
		container.append(user);
		
		{
			let button = user.querySelector('.button-heart');
			button.value = comment.like_count;

			button.onclick = () => {
				if (!config.user) {
					if (!config.bot) {
						router.redirect('/login', { url: router.getURL() });
					} else {
						router.noIndex();
					}
					
					return;
				}
				
				if (config.offline) {
					toast(__('You are currently offline.'));
					return;
				}
				
				button.classList.toggle('liked');

				let like = button.classList.contains('liked'), value = +button.value || 0;
					
				if (like) {
					button.value = value + 1;
				} else if (value) {
					button.value = value - 1;
				}
				
				if (value == 1) {
					likes.innerHTML = _e('1 like');
				} else {
					likes.innerHTML = _e('%s likes', formatNumber(value));
				}
					
				let bullet = likes.previousSibling;
				
				bullet.classList.toggle('hidden', !value);
				likes.classList.toggle('hidden', !value);
				
				if (like) {
					popupReview();

					api.post({
						url: '/comment/' + comment.id + '/like',
						success: likeComment,
						error: undoLike
					});
				} else {
					api.delete({
						url: '/comment/' + comment.id + '/like',
						success: likeComment,
						error: undoLike
					});
				}
				
				function likeComment (result) {
					let value = result.like_count;
					button.value = value;
					
					if (value == 1) {
						likes.innerHTML = _e('1 like');
					} else {
						likes.innerHTML = _e('%d likes', formatNumber(value));
					}
					
					bullet.classList.toggle('hidden', !value);
					likes.classList.toggle('hidden', !value);
				}
				
				function undoLike () {
					button.value = value;
					button.classList.toggle('liked', !like);
							
					if (value == 1) {
						likes.innerHTML = _e('1 like');
					} else {
						likes.innerHTML = _e('%d likes', formatNumber(value));
					}
							
					bullet.classList.toggle('hidden', !value);
					likes.classList.toggle('hidden', !value);
				}
			}
		}
		
		let content = createElement('div', 'content');
		
		if (comment.comment) {
			content.innerHTML = comment.comment;
			container.append(content);
		}
			
		if (comment.image || comment.giphy) {
			let tagName = comment.image ? 'div' : 'a';
			
			let outer = createElement(tagName, 'image');
			
			if (comment.giphy) {
				Object.assign(outer, { href: comment.giphy, target: '_blank' });
				outer.className += ' giphy';
			}
			
			outer.style.width = comment.width + 'px';

			let inner = createElement('span', 'inner zoomable');
			
			preload(comment.image || comment.giphy, (url) => inner.style.backgroundImage = 'url(' + url + ')');
			
			inner.style.paddingBottom = (comment.height / comment.width * 100) + '%';
			
			outer.append(inner);
			container.append(outer);
		}

		let meta = createElement('div', 'meta');
		meta.append(renderDatetime(comment.created));
			
		let bullet = createElement('span', 'bullet');
		
		let href = '/comment/' + comment.id + '/likes';
		
		let likes = createElement('a', 'likes');
		likes.href = href;
			
		likes.onclick = (e) => {
			e.preventDefault();
			e.stopPropagation();

			if (config.offline) {
				toast(__('You are currently offline.'));
				return;
			}

			router.go(href, { reset: true });
		}
			
		if (comment.like_count == 1) {
			likes.append(__('1 like'));
		} else {
			likes.append(__('%s likes', formatNumber(comment.like_count)));
		}

		if (!comment.like_count) {
			bullet.className += ' hidden';
			likes.className += ' hidden';
		}

		meta.append(bullet, likes);
					
		if (comment.last_modified) {
			let modified = createElement('span', 'modified', __('Modified'));
			modified.title = formatDateLocal(comment.last_modified, 'readable-datetime');
			
			meta.append(createElement('span', 'bullet'), modified);
		}
			
		if (post && post.comments == 'yes') {
			let button = createElement('button', 'button-reply', __('Reply'));
			
			button.onclick = () => {
				if (!config.user) {
					router.redirect('/login', { url: '/comments/' + post.id });
					return;
				}
				
				if (config.offline) {
					toast(__('You are currently offline.'));
					return;
				}

				{
					let elements = activeView.content.querySelectorAll('.edit-comment');
					
					for (let element of elements) {
						element.remove();
					}
				}
						
				let replies;

				if (comment.parent_id) {
					replies = container.parentNode;
				} else {
					replies = container.lastChild;
				}
				
				let compose = replies.lastChild, textarea = compose.querySelector('textarea');
				
				if (comment.user.username && comment.user.id != config.user.id) {
					textarea.value = '@' + comment.user.username + ' ';
				} else {
					textarea.value = '';
				}
				
				{
					let elements = activeView.content.querySelectorAll('.replies > .compose');
					
					for (let element of elements) {
						element.classList.toggle('hidden', element != compose);
					}
				}
					
				if (!config.mobile) {				
					let rect = textarea.getBoundingClientRect();
					
					activeView.scrollTop += rect.top - (window.innerHeight - rect.height) / 2;
					activeView.updateScroll();
				}
				
				textarea.focus();
			}
			
			meta.append(createElement('span', 'bullet'), button);
		}
		
		{
			let currentUser = false;
			
			if (config.user && config.user.id == comment.user.id) {
				currentUser = true;
			}

			let button = createElement('button', 'button-menu');
			
			button.onclick = () => {
				contextMenu(button, [
					{
						text: __('Edit comment'),
						hidden: !comment.comment || !~comment.permissions.indexOf('edit'),
						click: editComment
					}, {
						text: __('Delete comment'),
						hidden: !~comment.permissions.indexOf('delete'),
						click: deleteComment
					}, {
						text: __('Copy comment'),
						hidden: !comment.comment,
						click: () => {
							clipboard(content.innerText);
							toast(__('Comment copied to clipboard.'));
						}
					}, {
						text: __('Report user or comment'),
						hidden: currentUser,
						href: '/report/comment/' + comment.id
					}, {
						text: __('Unfollow user'),
						hidden: currentUser || comment.user.following != 'yes',
						click: () => api.unfollow(comment.user.id)
					}, {
						text: __('Cancel follow'),
						hidden: currentUser || comment.user.following != 'pending',
						click: () => api.cancel(comment.user.id)
					}, {
						text: __('Block user'),
						hidden: currentUser,
						click: () => api.confirm(__('Are you sure you want to block %s?', getUserName(comment.user)), () => api.block(comment.user))
					}
				]);
			}
			
			function editComment () {
				let element = content.previousSibling;
				
				if (element.className == 'edit-comment') {
					element.querySelector('textarea').focus();
					return;
				}
				
				let elements = activeView.content.querySelectorAll('.edit-comment');
				
				for (let element of elements) {
					element.remove();
				}
				
				let editor = createElement('form', 'edit-comment');
										
				let textarea = createElement('textarea', 'autofill');
				Object.assign(textarea, { spellcheck: false, value: decodeHTML(comment.comment) });
				
				textarea.onkeydown = (e) => {
					if (e.which == 27) {
						e.preventDefault();
						editor.remove();
					}
				}
				
				editor.append(textarea);
				
				let buttonSave = createElement('button', 'button-save', __('Save'));
				
				buttonSave.onclick = () => {
					if (config.offline) {
						toast(__('You are currently offline.'));
						return;
					}
					
					saveComment();
				}
				
				let buttonCancel = createElement('button', 'button-cancel', __('Cancel'));
				
				buttonCancel.onclick = () => {
					editor.remove();
				}
				
				editor.append(buttonSave, createElement('span', 'bullet'), buttonCancel);
				
				function saveComment () {
					let value = textarea.value.trim();
					
					if (!value) {
						api.alert(__('A comment cannot be empty.'), () => textarea.focus());
						return;
					}
					
					api.post({
						url: '/comment/' + comment.id,
						data: { comment: value },
						success: (result) => {
							let elements = document.querySelectorAll('.comment-' + comment.id);
							
							for (let element of elements) {
								element.replaceWith(renderComment(result.comment, post));
							}
						},
						complete: () => editor.remove()
					});
					
					textarea.disabled = true;
				}
				
				content.before(editor);
				
				textarea.focus();
			}
			
			function deleteComment () {
				api.confirm(__('Are you sure you want to delete this comment?'), () => {
					api.delete({
						url: '/comment/' + comment.id,
						success: () => {
							let elements = document.querySelectorAll('.comment-' + comment.id);
							
							for (let element of elements) {
								element.remove();
							}
						}
					});
				});
			}
			
			meta.append(button);
		}
		
		container.append(meta);
			
		if (!comment.parent_id) {
			container.append(renderComments(comment, post));
		}
			
		return container;
	}
	
	function renderCommentForm (values, post) {
		let container = createElement('form', 'compose');

		if ('reply_count' in values) {
			container.className += ' hidden';
		}

		let inner = createElement('div', 'inner');
		container.append(inner);

		let textarea = createElement('textarea', 'autofill');
		textarea.spellcheck = false;

		if ('comment_count' in values) {
			textarea.placeholder = __('Type a comment');
		} else {
			textarea.placeholder = __('Type a reply');
		}

		let attachedImage, attachedURL, attachedGiphy, width, height;

		textarea.onkeydown = (e) => {
			if (e.which == 13 && !e.shiftKey && !textarea.disabled) {
				e.preventDefault();
				sendComment();
			}
		}

		let buttonSend = createElement('button', 'button-send');

		buttonSend.onclick = () => {
			if (textarea.disabled) {
				return;
			}
			
			if (config.offline) {
				toast(__('You are currently offline.'));
				return;
			}
			
			sendComment();
		}

		let buttonImage = createElement('button', 'button-image');

		buttonImage.onclick = () => {
			gallery((file) => {
				loadImage(file, (image) => {
					if (!image || image.type == 'error') {
						api.alert(__('Could not open image.'));
						return;
					}
					
					let options = {
						maxWidth: 1080, maxHeight: 1080, canvas: true, orientation: true
					};
					
					if (image.width / image.height < .55) {
						options.aspectRatio = .55;
					}
					
					loadImage(file, (canvas) => {
						canvas.toBlob((blob) => {
							if (attachedImage || attachedGiphy) {
								textarea.previousSibling.remove();
							}
							
							attachedGiphy = null, width = null, height = null;
							
							if (attachedImage) {
								URL.revokeObjectURL(attachedURL);
							}
															
							attachedImage = blob, attachedURL = URL.createObjectURL(blob);
							
							let outer = createElement('div', 'image');
							outer.setAttribute('data-blob-url', attachedURL);
							outer.style.width = canvas.width + 'px';
							
							let inner = createElement('div', 'inner zoomable');
							Object.assign(inner.style, { backgroundImage: 'url(' + attachedURL + ')', paddingBottom: (canvas.height / canvas.width * 100) + '%' });
							
							let buttonDelete = createElement('button', 'button-delete');
							
							buttonDelete.onclick = () => {
								outer.remove();
								attachedImage = null;
								URL.revokeObjectURL(attachedURL);
							}
							
							outer.append(inner, buttonDelete);
							
							textarea.before(outer);
						}, 'image/jpeg');
					}, options);
				}, { orientation: true });
			}, false);
		}

		let buttonGiphy = createElement('button', 'button-giphy');

		buttonGiphy.onclick = () => {
			giphy((image) => {
				if (attachedImage || attachedGiphy) {
					textarea.previousSibling.remove();
				}
				
				if (attachedImage) {
					URL.revokeObjectURL(attachedURL);
					attachedImage = null;
				}
				
				attachedGiphy = image.url, width = image.width, height = image.height;
				
				let outer = createElement('div', 'image');
				outer.style.width = width + 'px';
				
				let inner = createElement('div', 'inner zoomable');
				Object.assign(inner.style, { backgroundImage: 'url(' + attachedGiphy + ')', paddingBottom: (height / width * 100) + '%' });
				
				let buttonDelete = createElement('button', 'button-delete');
				
				buttonDelete.onclick = () => {
					outer.remove();
					attachedGiphy = null, width = null, height = null;
				}
				
				outer.append(inner, buttonDelete);
				
				textarea.before(outer);
			});
		}
		
		function sendComment () {
			let value = textarea.value.trim();
			
			if (!value && !attachedImage && !attachedGiphy) {
				api.alert(__('A comment cannot be empty.'), () => textarea.focus());
				return;
			}
			
			api.post({
				url: '/comment',
				data: { post_id: post.id, parent_id: values.id, comment: value, image: attachedImage, giphy: attachedGiphy, width: width, height: height },
				success: (result) => {
					let comment = result.comment;
					
					api.get({
						url: '/comments/' + post.id,
						data: { parent_id: values.id, since_id: values.id ? lastComment : firstComment, until_id: comment.id},
						success: (result) => {
							let fragment = document.createDocumentFragment();
							
							for (let comment of result.comments) {
								fragment.append(renderComment(comment, post));
							}
							
							let element = renderComment(comment, post);
							
							if (values.id) {
								fragment.append(element);
								
								if (container.previousSibling == buttonMore) {
									buttonMore.replaceWith(fragment);
								} else {
									container.before(fragment);
								}
								
								lastComment = comment.id;
							} else {
								fragment.prepend(element);
								
								if (container.firstChild == buttonPrev) {
									buttonPrev.replaceWith(fragment);
								} else {
									container.parentNode.prepend(fragment);
								}
								
								firstComment = comment.id;
							}
							
							let rect = element.getBoundingClientRect();
							
							activeView.scrollTop += rect.top - (window.innerHeight - rect.height) / 2;
							activeView.updateScroll();
						}
					});
					
					if ('reply_count' in values) {
						container.classList.add('hidden');
					}
					
					let elements = document.querySelectorAll('.post-' + post.id + ' .button-comment');
					
					for (let element of elements) {
						element.value = result.comment_count;
						element.nextSibling.innerHTML = formatNumber(result.comment_count);
					}
				},
				complete: () => {
					if ('reply_count' in values) {
						container.classList.add('hidden');
					}
					
					attachedGiphy = null, width = null, height = null;
					
					if (textarea.previousSibling) {
						textarea.previousSibling.remove();
					}
					
					Object.assign(textarea, { value: '', disabled: false });
				}
			});
			
			if (attachedImage) {
				attachedImage = null;
				URL.revokeObjectURL(attachedURL);
			}
			
			textarea.disabled = true;
			
			if (document.activeElement && document.activeElement.blur) {
				document.activeElement.blur();
			}
		}	

		inner.append(textarea, buttonSend, buttonImage, buttonGiphy);	
		
		return container;
	}
	
	return container;
}
