import { DOCUMENT } from '@angular/common';
import { Component, EventEmitter, Inject, OnInit, Output, Input } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { interval, Subject, Subscription } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import { type } from 'os';
import { SimpleChanges } from '@angular/core';
import { ToCEntry, ToCEntryType } from '../dataset-description.component';
import { FormArray, FormGroup } from '@angular/forms';
import { VisibilityRulesService } from '../visibility-rules/visibility-rules.service';
import { Rule } from '@app/core/model/dataset-profile-definition/rule';

export interface Link {
	/* id of the section*/
	id: string;
	/* header type h3/h4 */
	type: string;
	/* If the anchor is in view of the page */
	active: boolean;
	/* name of the anchor */
	name: string;
	/* top offset px of the anchor */
	top: number;
	page: number;
	section: number;
	show: boolean;
	selected: boolean;
}

@Component({
	selector: 'table-of-contents',
	styleUrls: ['./table-of-contents.scss'],
	templateUrl: './table-of-contents.html'
})
export class TableOfContents extends BaseComponent implements OnInit {

	@Input() links: Link[];
	container: string;
	headerSelectors = '.toc-page-header, .toc-section-header, .toc-compositeField-header';
	@Output() stepFound = new EventEmitter<LinkToScroll>();
	@Output() currentLinks = new EventEmitter<Link[]>();
	subscription: Subscription;
	linksSubject: Subject<HTMLElement[]> = new Subject<HTMLElement[]>();

	@Input() isActive: boolean;

	tocentries: ToCEntry[] = null;
	@Input() TOCENTRY_ID_PREFIX = '';
	// visibilityRules:Rule[] = [];
	@Input() visibilityRules:Rule[] = [];
	@Input() showErrors: boolean = false;

	private _tocentrySelected:ToCEntry = null;
	get tocentrySelected(){
	
		return this.hasFocus?this._tocentrySelected: null;
	}
	set tocentrySelected(value){
		this._tocentrySelected = value;
	}

	@Input() formGroup: FormGroup;
	@Input() hasFocus: boolean = false;
	show: boolean = false;

	constructor(
		@Inject(DOCUMENT) private _document: Document,
		public visibilityRulesService: VisibilityRulesService
		) {
		super();
	}


	ngOnInit(): void {
		
		if(this.formGroup){
			this.tocentries = this.getTocEntries(this.formGroup.get('datasetProfileDefinition'));
			const fg = this.formGroup.get('datasetProfileDefinition');
			this.visibilityRulesService.buildVisibilityRules(this.visibilityRules, fg);
			
		}else{
			
			//emit value every 500ms
			const source = interval(500);
			this.subscription = source.subscribe(val => {
				const headers = Array.from(this._document.querySelectorAll(this.headerSelectors)) as HTMLElement[];
				this.linksSubject.next(headers);
			});

			if (!this.links || this.links.length === 0) {
				this.linksSubject.asObservable()
					.pipe(distinctUntilChanged((p: HTMLElement[], q: HTMLElement[]) => JSON.stringify(p) == JSON.stringify(q)))
					.subscribe(headers => {
						const links: Array<Link> = [];

						if (headers.length) {
							let page;
							let section;
							let show
							for (const header of headers) {
								let name;
								let id;
								if (header.classList.contains('toc-page-header')) {  // deprecated after removing stepper
									name = header.innerText.trim().replace(/^link/, '');
									id = header.id;
									page = header.id.split('_')[1];
									section = undefined;
									show = true;
								} else if (header.classList.contains('toc-section-header')) {
									name = header.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].nodeValue.trim().replace(/^link/, '');
									id = header.id;
									page = header.id.split('.')[1];
									section = header.id;
									if (header.id.split('.')[4]) { show = false; }
									else { show = true; }
								} else if (header.classList.contains('toc-compositeField-header')) {
									name = (header.childNodes[0]).nodeValue.trim().replace(/^link/, '');
									id = header.id;
									// id = header.parentElement.parentElement.parentElement.id;
									show = false;
								}
								const { top } = header.getBoundingClientRect();
								links.push({
									name,
									id,
									type: header.tagName.toLowerCase(),
									top: top,
									active: false,
									page: page,
									section: section,
									show: show,
									selected: false
								});
							}
						}
						this.links = links;
						// Initialize selected for button next on dataset wizard component editor
						this.links.length > 0 ? this.links[0].selected = true : null;
					})
			}
		}

	}

	ngOnChanges(changes: SimpleChanges) {
		// if (!this.isActive && this.links && this.links.length > 0) {
		// 	this.links.forEach(link => {
		// 		link.selected = false;
		// 	})
		// 	this.links[0].selected = true;
		// }
	}

	goToStep(link: Link) {
		this.stepFound.emit({
			page: link.page,
			section: link.section
		});
		this.currentLinks.emit(this.links);

		setTimeout(() => {
			const target = document.getElementById(link.id);
			target.scrollIntoView(true);

			var scrolledY = window.scrollY;
			if (scrolledY) {
				window.scroll(0, scrolledY - 70);
			}
		}, 500);
	}

	toggle(headerLink: Link) {
		const headerPage = +headerLink.name.split(" ", 1);
		let innerPage;
		for (const link of this.links) {
			link.selected = false;
			if (link.type === 'mat-expansion-panel') {
				innerPage = +link.name.split(".", 1)[0];
				if (isNaN(innerPage)) { innerPage = +link.name.split(" ", 1) }
			} else if (link.type === 'h5') {
				innerPage = +link.name.split(".", 1)[0];
			}
			if (headerPage === innerPage && (link.type !== 'mat-expansion-panel' || (link.type === 'mat-expansion-panel' && link.id.split(".")[4]))) {
				link.show = !link.show;
			}
		}
		headerLink.selected = true;
	}

	// getIndex(link: Link): number {
	// 	return +link.id.split("_", 2)[1];
	// }


	private _buildRecursively(form: FormGroup,whatAmI:ToCEntryType):ToCEntry{
		if(!form) return null;

		switch(whatAmI){
			case ToCEntryType.Section:
				const sections = form.get('sections') as FormArray;
				const fieldsets = form.get('compositeFields') as FormArray;


				const tempResult:ToCEntry[] = [];
				
				if(sections &&sections.length){
					sections.controls.forEach(section=>{
						tempResult.push(this._buildRecursively(section as FormGroup, ToCEntryType.Section));
					});

				}else if(fieldsets && fieldsets.length){
					fieldsets.controls.forEach(fieldset=>{
						tempResult.push(this._buildRecursively(fieldset as FormGroup, ToCEntryType.FieldSet));
					});
				}
				return {
					form: form,
					id: form.get('id').value,
					label: form.get('title').value,
					numbering: '',
					subEntries:tempResult,
					subEntriesType: sections &&sections.length? ToCEntryType.Section: ToCEntryType.FieldSet,
					type: ToCEntryType.Section,
					ordinal: form.get('ordinal').value
				}
			case ToCEntryType.FieldSet:
				return {
					form: form,
					label:form.get('title').value,
					id: form.get('id').value,
					numbering:'s',
					subEntries:[],
					subEntriesType: ToCEntryType.Field,
					type: ToCEntryType.FieldSet,
					ordinal: form.get('ordinal').value
				}
		}
	}

	private _sortByOrdinal(tocentries: ToCEntry[]){
		
		if(!tocentries || !tocentries.length) return;
		
		tocentries.sort(this._customCompare);
		tocentries.forEach(entry=>{
			this._sortByOrdinal(entry.subEntries);
		});
	}

	private _customCompare(a,b){
		return a.ordinal - b.ordinal;
	}

	private _calculateNumbering(tocentries: ToCEntry[], depth:number[] = []){
		if(!tocentries || !tocentries.length){
			return;
		}

		let  prefixNumbering = depth.length? depth.join('.'): '';

		if(depth.length) prefixNumbering = prefixNumbering+".";
		tocentries.forEach((entry,i)=>{
			entry.numbering = prefixNumbering + (i+1);
			this._calculateNumbering(entry.subEntries, [...depth, i+1])
		});
	}

	
	getTocEntries(form): ToCEntry[] {
		if (form == null) { return []; }
		const result: ToCEntry[] = [];

		//build parent pages
		(form.get('pages') as FormArray).controls.forEach((pageElement, i) => {
			result.push({
				id: i+'id',
				label: pageElement.get('title').value,
				type: ToCEntryType.Page,
				form: pageElement,
				numbering: (i + 1).toString(),
				subEntriesType: ToCEntryType.Section,
				subEntries:[],
				ordinal: pageElement.get('ordinal').value
			} as ToCEntry)
		});



		result.forEach((entry,i)=>{

			const sections = entry.form.get('sections') as FormArray;

			sections.controls.forEach(section=>{
				const tempResults = this._buildRecursively(section as FormGroup,ToCEntryType.Section);
				entry.subEntries.push(tempResults);
			});

		});

		this._sortByOrdinal(result);
		//calculate numbering
		this._calculateNumbering(result);
		return result;

	}

	onToCentrySelected(entry: ToCEntry){
		this.tocentrySelected = entry;
		// console.log('entry selected', entry);
	}


	public seekToFirstElement(){//only on tocentry mode
		if(this.tocentries && this.tocentries.length){
			this.tocentrySelected = this.tocentries[0];
		}
	}

}

export interface LinkToScroll {
	page: number;
	section: number;
}
