import { Component, OnInit, SecurityContext, ViewChild } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { Router, ActivatedRoute, RoutesRecognized, NavigationEnd } from '@angular/router';

import { Post, Category, Image } from '../../core/entities';
import { AuthService } from '../../core/services/auth.service';
import { CreateUpdatePostService } from './create-update-post.service';

import { filter, pairwise } from 'rxjs/operators';

import { HttpEventType, HttpResponse } from '@angular/common/http';

enum Tabs {
	EDITION = 'Édition',
	PREVIEW = 'Aperçu'
}

declare let Prism: any;

@Component({
	selector: 'app-create-update-post',
	templateUrl: './create-update-post.component.html',
	styleUrls: ['./create-update-post.component.scss']
})
export class CreateUpdatePostComponent implements OnInit {
	static INPUT_POST_TEXT = 'text';

	@ViewChild('frameCode') public contentModal;
	@ViewChild('frameImages') public imagesModal;

	model: Post = new Post('', '', '', '', '', null, null, null);
	parsedPost: Post;

	listCategories: Array<Category>;

	activatedTab: string;

	modelError: string;
	result: string;

	// Variables for the code popup
	codeTmp: string;
	languageTmp: string;
	codeError: string;

	// Variables for the images popup
	imagesLoaded: boolean;
	listImages: Array<Image>;
	selectedFiles: FileList;
	currentFileUpload: File;
	progress: { percentage: number } = { percentage: 0 };

	constructor(
		private createUpdatePostService: CreateUpdatePostService,
		private activatedRoute: ActivatedRoute,
		private sanitizer: DomSanitizer,
		private router: Router,
		private authService: AuthService
	) {
		this.imagesLoaded = false;
	}

	ngOnInit(): void {
		this.listCategories = [];
		this.activatedTab = Tabs.EDITION;
		this.createUpdatePostService.getCategories().subscribe(listCategories => {
			this.listCategories = listCategories.filter(category => !category.listSubCategories.length);
		});

		const postKey = this.activatedRoute.snapshot.paramMap.get('postKey');
		if (postKey) {
			this.createUpdatePostService.getPost(postKey).subscribe(post => {
				if (post.author.key === this.authService.getUser().key) {
					this.model = post;
				} else {
					this.router.navigate(['/forbidden']);
				}
			});

			// FIXME: The message isn't shown and the method ngOnInit is too much called, also during others components navigation.
			this.router.events.pipe(filter(e => e instanceof RoutesRecognized), pairwise()).subscribe((events: any) => {
				if (events[0].urlAfterRedirects === '/posts/new') {
					this.setMessage('Article créé.', false);
				}
			});
		}
	}

	activateEdition(): void {
		this.activatedTab = Tabs.EDITION;
	}

	activatePreview(): void {
		this.activatedTab = Tabs.PREVIEW;
		this.parsedPost = undefined;
		this.createUpdatePostService.processPreview(this.model).subscribe(parsedPost => {
			this.parsedPost = parsedPost;
			setTimeout(() => {
				Prism.highlightAll();
			}, 100);
		});
	}

	getContent(): string {
		return this.sanitizer.sanitize(SecurityContext.HTML, this.parsedPost.text);
	}

	injectHeader(header: string): void {
		this.injectElement('[' + header + '][/' + header + ']', 4);
	}

	injectLink(): void {
		this.injectElement('[link href="" txt="" /]', 11);
	}

	injectCode(): void {
		if (this.languageTmp && this.codeTmp) {
			const codeExtract = '\n[code lg="' + this.languageTmp + '"]\n'
					+ this.codeTmp + '\n[/code]\n\n';

			this.injectElement(codeExtract, codeExtract.length);

			this.contentModal.hide();

			this.codeTmp = undefined;
			this.languageTmp = undefined;
			this.resetSelect('languageTmp');
		} else {
			this.codeError = 'Le langage et l\'extrait de code doivent être renseignés.';
			setTimeout(() => {
				this.codeError = undefined;
			}, 3500);
		}
	}

	private injectElement(elementToInject: string, lengthForCursor: number): void {
		const input = <HTMLInputElement>document.getElementById(CreateUpdatePostComponent.INPUT_POST_TEXT);
		const contentValue = input.value;
		const cursorPosition = input.selectionStart;

		this.model.text = contentValue.slice(0, cursorPosition) + elementToInject + contentValue.slice(cursorPosition);
		input.focus();
		const newCursor = cursorPosition + lengthForCursor;
		input.selectionStart = newCursor;
		input.selectionEnd = newCursor;
	}

	private resetSelect(selectId: string): void {
		const select = <HTMLSelectElement>document.getElementById(selectId);
		select.selectedIndex = 0;
	}

	save(): void {
		if (this.model.title && this.model.image && this.model.description && this.model.category && this.model.text) {
			this.model.author = this.authService.getUser();

			if (this.model.key) {
				this.createUpdatePostService.updatePost(this.model).subscribe(post => {
					this.setMessage('Modification enregistrée', false);
				});
			} else {
				this.createUpdatePostService.addPost(this.model).subscribe(post => {
					this.router.navigate([`/posts/update/${post.key}`]);
				});
			}
		} else {
			this.setMessage('Veuillez saisir les champs obligatoires.', true);
		}
	}

	setMessage(message: string, error: boolean): void {
		this[error ? 'modelError' : 'result'] = message;

		const resultMsgDiv = document.getElementById(error ? 'errorMsg' : 'resultMsg');
		resultMsgDiv.style.maxHeight = '64px';

		setTimeout(() => {
			resultMsgDiv.style.maxHeight = '0px';
			setTimeout(() => {
				this[error ? 'modelError' : 'result'] = undefined;
			}, 550);
		}, 3000);
	}

	openImagesModal(): void {
		this.imagesLoaded = false;
		this.imagesModal.show();

		this.createUpdatePostService.getImages().subscribe(listImages => {
			this.listImages = listImages;
			this.imagesLoaded = true;
		});
	}

	getLinkSrc(pLink: string): string {
		return `/api/images/${pLink}`;
	}

	openNewImageInput(): void {
		document.getElementById('newImageInput').click();
	}

	uploadImage(event): void {
		this.selectedFiles = event.target.files;
		this.progress.percentage = 0;

		this.currentFileUpload = this.selectedFiles.item(0);
		// This prevents error 400 if user doesn't select any file to upload and close the input file.
		if (this.currentFileUpload) {
			this.createUpdatePostService.uploadPicture(this.currentFileUpload).subscribe(result => {
				if (result.type === HttpEventType.UploadProgress) {
					this.progress.percentage = Math.round(100 * result.loaded / result.total);
				} else if (result instanceof HttpResponse) {
					console.log('File ' + result.body + ' completely uploaded!');
					this.createUpdatePostService.getImageDetails(result.body as string).subscribe(image => {
						this.listImages.push(image);
					});
				}
				this.selectedFiles = undefined;
			});
		}
	}

	injectImage(pImageLink: string): void {
		const imgTag = `[img src="${this.getLinkSrc(pImageLink)}" /]`;
		this.injectElement(imgTag, imgTag.length);
		this.imagesModal.hide();
	}

	compareCategories(cat1: Category, cat2: Category): boolean {
		return cat1 && cat2 ? cat1.id === cat2.id : cat1 === cat2;
	}
}
