import { DOCUMENT } from '@angular/common';
import { Component, EventEmitter, Inject, OnInit, Output, Input, AfterViewInit } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { interval, Subject, Subscription } from 'rxjs';
import { distinctUntilChanged, filter, takeUntil } from 'rxjs/operators';
import { type } from 'os';
import { SimpleChanges } from '@angular/core';
import { NewEntryType, TableUpdateInfo, ToCEntry, ToCEntryType } from './table-of-contents-entry';
import { DragulaService } from 'ng2-dragula';
import { FormArray } from '@angular/forms';
import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
import { TranslateService } from '@ngx-translate/core';
import { ContentObserver } from '@angular/cdk/observers';


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

	@Input() links: ToCEntry[];
	@Input() itemSelected: ToCEntry;
	@Input() colorizeInvalid: boolean = false;
	@Input() viewOnly: boolean;


	@Output() itemClick = new EventEmitter<ToCEntry>();
	// @Output() newEntry = new EventEmitter<ToCEntry>();
	@Output() removeEntry = new EventEmitter<ToCEntry>();
	@Output() createEntry = new EventEmitter<NewEntryType>();
	@Output() dataNeedsRefresh = new EventEmitter<TableUpdateInfo>();

	isDragging: boolean = false;
	draggingItemId: string = null;
	tocEntryType = ToCEntryType;

	DRAGULA_ITEM_ID_PREFIX="table_item_id_";
	ROOT_ID: string = "ROOT_ID";//no special meaning
	private _dragStartedAt;
	private VALID_DROP_TIME = 500;//ms
	overcontainer: string = null;

	$clock = interval(10);
	scrollTableTop = false;
	scrollTableBottom = false;
	pxToScroll = 15;

	constructor(
		@Inject(DOCUMENT) private _document: Document,
		private dragulaService: DragulaService,
		private snackbar: MatSnackBar,
		private language: TranslateService
		) {
		super();
		
		if(this.dragulaService.find('TABLEDRAG')){
			this.dragulaService.destroy('TABLEDRAG');
		}

		const dragula = this.dragulaService.createGroup('TABLEDRAG', {});

		const drake = dragula.drake;

		drake.on('drop', (el, target, source,sibling)=>{

			if(this._dragStartedAt){
				const timeNow = new Date().getTime();

				if(timeNow - this._dragStartedAt> this.VALID_DROP_TIME){
					// console.log('timenow: ', timeNow);
					// console.log('timestarted', this._dragStartedAt);
					this._dragStartedAt = null;
					
				}else{
					this.dataNeedsRefresh.emit();// even though the data is not changed the TABLE DRAG may has changed
					return;
				}
			}else{
				this.dataNeedsRefresh.emit();// even though the data is not changed the TABLE DRAG may has changed
				return;
				
			}

			const elementId = (el.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX,'');
			const targetId = target.id as string;
			const sourceId = source.id as string;


			if(!(elementId && targetId && sourceId)){
				console.info('Elements do not have an id');
				this.dataNeedsRefresh.emit();
				return;
			}


			const element:ToCEntry = this._findTocEntryById(elementId, this.links);
			const targetContainer:ToCEntry = this._findTocEntryById(targetId , this.links);
			const sourceContainer:ToCEntry = this._findTocEntryById(sourceId, this.links);

			if(!(element && (targetContainer ||((element.type===ToCEntryType.Page) && (targetId === this.ROOT_ID))) && (sourceContainer||((element.type===ToCEntryType.Page) && (sourceId === this.ROOT_ID))))){
				// console.info('Could not find elements');
				this.dataNeedsRefresh.emit();
				drake.cancel(true);
				return;
			}


			switch(element.type){
				case ToCEntryType.FieldSet:{
					if(targetContainer.type != this.tocEntryType.Section){
						// const message = 'Fieldset can only be child of Subsections';
						const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.FIELDSET-MUST-HAVE-PARENT-SECTION');
						// console.error(message);
						this.notifyUser(message)
						this.dataNeedsRefresh.emit();
						return;
					}

					//check if target container has no sections
					if((targetContainer.form.get('sections') as FormArray).length){
						// const message = 'Cannot have inputs and sections on the same level';
						const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.INPUT-SECTION-SAME-LEVEL');
						this.notifyUser(message);
						// console.error(message);
						this.dataNeedsRefresh.emit();
						return;
					}

					const fieldsetForm = element.form;
					const targetFieldsets = targetContainer.form.get('fieldSets') as FormArray;
					const sourceFieldsets = sourceContainer.form.get('fieldSets') as FormArray;

					if(!targetFieldsets){
						console.info('Not target fieldsets container found');
						this.dataNeedsRefresh.emit();
						return;
					}

					let sourceOrdinal=-1;
					let idx = -1;
					sourceFieldsets.controls.forEach((elem,index)=>{
						if(elem.get('id').value === elementId){
							sourceOrdinal = elem.get('ordinal').value;
							idx = index
						}
					});

					if(sourceOrdinal>=0  && idx>=0){
						sourceFieldsets.removeAt(idx);

						sourceFieldsets.controls.forEach(control=>{
							const ordinal = control.get('ordinal');
							if((ordinal.value>= sourceOrdinal) && sourceOrdinal>0){
								const updatedOrdinalVal = ordinal.value -1;
								ordinal.setValue(updatedOrdinalVal);
							}
						});
						sourceFieldsets.controls.sort(this._compareOrdinals);
					}

					let position:number = targetFieldsets.length;

					if(!sibling ||!sibling.id){
						console.info('No sibling Id found');
					}else{
						const siblingId = (sibling.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX,'');
						let siblingIndex = -1;
						targetFieldsets.controls.forEach((e,idx)=>{
							if(e.get('id').value === siblingId){
								siblingIndex = idx;
								position = e.get('ordinal').value;
							}
							
						});

						if(siblingIndex>=0){ //sibling found
							
							targetFieldsets.controls.filter(control=> control.get('ordinal').value >= position).forEach(control=>{
								const ordinal = control.get('ordinal');
								const updatedOrdinalVal = ordinal.value +1;
								ordinal.setValue(updatedOrdinalVal);
							})
						}
						
					}


					fieldsetForm.get('ordinal').setValue(position);
					targetFieldsets.insert(position,fieldsetForm);
					targetFieldsets.controls.sort(this._compareOrdinals);
					this.dataNeedsRefresh.emit({draggedItemId: elementId});

					break;
				}
				case ToCEntryType.Section:{

					if(targetContainer.type == ToCEntryType.Section){
						if((targetContainer.form.get('fieldSets')as FormArray).length){
							// const message = 'Cannot have inputs and sections on the same level';
							const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.INPUT-SECTION-SAME-LEVEL');;
							this.notifyUser(message);
							// console.info(message);
							this.dataNeedsRefresh.emit();
							return;
						}

						const targetSections = targetContainer.form.get('sections') as FormArray;
						const elementSectionForm = element.form;
						const sourceSections = elementSectionForm.parent as FormArray;

						if(!(targetSections && sourceSections && elementSectionForm)){
							console.info('Could not load sections');
							this.dataNeedsRefresh.emit();
							return;
						}

						let idx = -1;
						sourceSections.controls.forEach((section,i)=>{
							if(section.get('id').value === elementId){
								idx = i;
							}
						});

						if(!(idx>=0)){
							console.info('Could not find element in Parent container');
							this.dataNeedsRefresh.emit();
							return;
						}

						sourceSections.controls.filter(control=>control.get('ordinal').value >= elementSectionForm.get('ordinal').value).forEach(control=>{
							const ordinal = control.get('ordinal');
							const updatedOrdinalVal = ordinal.value? ordinal.value -1: 0;
							ordinal.setValue(updatedOrdinalVal);
						});
						

						sourceSections.removeAt(idx);

						let targetOrdinal = targetSections.length;

						if(sibling && sibling.id){
							const siblingId = sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX,'');

							targetSections.controls.forEach((section,i)=>{
								if(section.get('id').value === siblingId){
									targetOrdinal = section.get('ordinal').value;
								}
							})

							// if(targetOrdinal!=targetSections.length){//mporei na einai idio
								// section.get('ordinal').setValue(i+1);
								targetSections.controls.filter(control=> control.get('ordinal').value>=targetOrdinal).forEach(control=>{
									const ordinal = control.get('ordinal');
									const updatedOrdinalVal = ordinal.value+1;
									ordinal.setValue(updatedOrdinalVal);
								});
							// }

						}else{
							console.info('no siblings found');
						}
						elementSectionForm.get('ordinal').setValue(targetOrdinal);
						targetSections.insert(targetOrdinal, elementSectionForm);

					}else if(targetContainer.type === ToCEntryType.Page){
						// const pageId = targetContainer.form.get('id').value;

						const rootform = targetContainer.form.root;
						const sectionForm = element.form;
						const parentSections = sectionForm.parent as FormArray;

						let parentIndex = -1;
						parentSections.controls.forEach((section,i )=>{
							if(section.get('id').value === elementId){
								parentIndex = i
							}
						})
						

						if(parentIndex<0){
							console.info('could not locate section in parents array');
							this.dataNeedsRefresh.emit();
							return;
						}

						//update parent sections ordinal
						parentSections.controls.filter(section=>section.get('ordinal').value >= sectionForm.get('ordinal').value).forEach(section=>{
							const ordinal = section.get('ordinal');
							const updatedOrdinalVal = ordinal.value?ordinal.value -1: 0;
							ordinal.setValue(updatedOrdinalVal);
						})

						parentSections.removeAt(parentIndex);



						let position = 0;
						if(targetContainer.subEntries){
							position = targetContainer.subEntries.length;
						}
						//populate sections
						const targetSectionsArray = rootform.get('sections') as FormArray;


						if(sibling && sibling.id){
							const siblingId= sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX, '');
							let indx = -1;

							targetContainer.subEntries.forEach((e,i)=>{
								if(e.form.get('id').value === siblingId){
									indx = i;
									position = e.form.get('ordinal').value;
								}
							});
							if(indx>=0) {
								
								// e.form.get('ordinal').setValue(i+1);
								targetContainer.subEntries.filter(e=>e.form.get('ordinal').value >= position).forEach(e=>{
									const ordinal = e.form.get('ordinal');
									const updatedOrdinalVal = ordinal.value +1;
									ordinal.setValue(updatedOrdinalVal);
								});
							}

						}else{	
							console.info('No sibling found');
						}

						sectionForm.get('ordinal').setValue(position);
						sectionForm.get('page').setValue(targetContainer.id);
						targetSectionsArray.push(sectionForm);

					}else{
						// const message = 'Drag not support to specific container';
						const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.DRAG-NOT-SUPPORTED');
						this.notifyUser(message);
						// console.info(message);
						this.dataNeedsRefresh.emit();
						return;
					}



					this.dataNeedsRefresh.emit({draggedItemId: elementId});
					break;
				}
				case ToCEntryType.Page:{
					if(targetId != this.ROOT_ID){
						// const message = 'A page element can only be at top level';
						const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.PAGE-ELEMENT-ONLY-TOP-LEVEL');
						this.notifyUser(message);
						// console.info(message);
						this.dataNeedsRefresh.emit();
						return;
					}

					const rootForm = element.form.root;
					if(!rootForm){
						console.info('Could not find root!')
						this.dataNeedsRefresh.emit();
						return;
					}


					const pages = rootForm.get('pages') as FormArray;
					const pageForm = element.form;

					let index = -1;

					pages.controls.forEach((page,i)=>{
						if(page.get('id').value === elementId){
							index =i;
						}
					});

					if(index<0){
						console.info('Could not locate page on pages');
						this.dataNeedsRefresh.emit();
						return;
					}


					//ordinality
					pages.controls.filter(page=> page.get('ordinal').value>= pageForm.get('ordinal').value).forEach(page=>{
						const ordinal = page.get('ordinal');
						const ordinalVal = ordinal.value? ordinal.value - 1: 0;
						ordinal.setValue(ordinalVal);						
					});

					pages.removeAt(index);

					let targetPosition = pages.length;

					if(sibling){
						const siblingId = sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX, '');

						pages.controls.forEach((page,i)=>{
							if(page.get('id').value === siblingId){
								targetPosition = page.get('ordinal').value;
							}
						});
					}
					pageForm.get('ordinal').setValue(targetPosition);

					//update ordinality
					pages.controls.filter(page=> page.get('ordinal').value>= targetPosition).forEach(page=>{
						const ordinal = page.get('ordinal');
						const ordinalVal = ordinal.value +1;
						ordinal.setValue(ordinalVal);						
					});


					pages.insert(targetPosition, pageForm);
					this.dataNeedsRefresh.emit({draggedItemId:elementId});
					break;
				}
				default:

					console.info('Could not support moving objects for specific type of element');
					this.dataNeedsRefresh.emit();
					return;

			}
		});


		drake.on('drag',(el,source)=>{
			this._dragStartedAt =  new Date().getTime();
			// console.log('drag fired');
			this.isDragging = true;
			this.draggingItemId = (el.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX, '');

			// setTimeout(() => {
			// 	if(this.isDragging){
			// 		this._scrollIntoDragginItem(this.draggingItemId);
			// 	}
			// }, this.VALID_DROP_TIME);
		});
		drake.on('over',(el, container, source)=>{
			try {
				this.overcontainer = container.id;
			} catch (error) {
				this.overcontainer = null;
			}
		});
		drake.on('dragend',(el)=>{
			this.isDragging = false;
			this.draggingItemId = null;
			this.overcontainer = null;
		});

		


	}
	ngAfterViewInit(): void {

		const top = document.querySelector('.top-scroller');
		const bottom = document.querySelector('.bottom-scroller');
		const tableDiv = document.querySelector('#tocentrytable');

		try {
			top.addEventListener('mouseover', (e) => {this.scrollTableTop = true; },{
				passive: true
			  });
			bottom.addEventListener('mouseover', (e) => {this.scrollTableBottom = true; },{
				passive: true
			  });
	
			top.addEventListener('mouseout', (e) => {this.scrollTableTop = false},{
				passive: true
			  });
			bottom.addEventListener('mouseout', (e) => {this.scrollTableBottom = false;},{
				passive: true
			  });
	
	
			this.$clock
			.pipe(
				takeUntil(this._destroyed),
				filter(() => this.scrollTableTop)
			)
			.subscribe(()=>{
				try{
					tableDiv.scrollBy(0, -this.pxToScroll);
				} catch {}
			});
			this.$clock
			.pipe(
				takeUntil(this._destroyed),
				filter(() => this.scrollTableBottom)
			)
			.subscribe(()=>{
				try{
					tableDiv.scrollBy(0, this.pxToScroll);
				} catch {}
			});
		} catch {
			console.log('could not find scrolling elements');
		}

		
	}

	private _scrollIntoDragginItem(id: string){

		// const table = document.getElementById('tocentrytable');
		// if(table){
		// 	// const element = document.getElementById('TABLE_ENTRY'+id);
		// 	console.log('Table found!');
		// 	const element = document.getElementById('TABLE_ENTRY' + id);
		// 	const elementFromTable = table.closest('#TABLE_ENTRY'+ id);


		// 	if(elementFromTable){
		// 		console.log('found from table:', elementFromTable);
		// 	}
		// 	if(element){
		// 		console.log('Element found!');
		// 		// element.classList.add('text-danger');
		// 		// console.log(element);

		// 		const tableRect = table.getBoundingClientRect();
		// 		const elementRect = element.getBoundingClientRect();
				

		// 		console.log('tablerect :',tableRect);
		// 		console.log('elementRect :',elementRect);

		// 			const dY = elementRect.top - tableRect.top;
		// 			console.log('Distance from table is ', dY);
		// 			// table.scroll({top:dY,behavior:'smooth'});
		// 			console.log('found from document ', element);

		// 			// element.scrollIntoView();
		// 	}
		// 	// element.scrollIntoView();
		// }
	}

	private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{
		if(!tocentries){
			return null;
		}

		let tocEntryFound = tocentries.find(entry=>entry.id === id);

		if(tocEntryFound){
			return tocEntryFound;
		}	

		for(let entry of tocentries){
			const result = this._findTocEntryById(id, entry.subEntries);
			if(result){
				tocEntryFound = result;
				break;
			}
		}
		
		return tocEntryFound? tocEntryFound: null;
	}

	ngOnInit(): void {
		

	}


	itemClicked(item: ToCEntry){
		//leaf node
		this.itemClick.emit(item);
	}
	
	// addNewEntry(tce: ToCEntry){
	// 	this.newEntry.emit(tce);
	// }
	deleteEntry(currentLink: ToCEntry){
		this.removeEntry.emit(currentLink);
	}

	createNewEntry(newEntry: NewEntryType){
		this.createEntry.emit(newEntry);
	}
	onDataNeedsRefresh(){
		this.dataNeedsRefresh.emit();
	}

	notifyUser(message:string){
		this.snackbar.open(message, null, this._snackBarConfig);
	}

	private _snackBarConfig: MatSnackBarConfig = {
		duration:2000
	}

	private _compareOrdinals(a, b){

		const aValue = a.get('ordinal').value as number;
		const bValue = b.get('ordinal').value as number;

		// if(!aValue || !bValue) return 0;
		return aValue - bValue;
	}


}
