
import { Injectable, NgZone } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { IBreadCrumbComponent } from '@app/ui/misc/breadcrumb/definition/IBreadCrumbComponent';
import { BaseService } from '@common/base/base.service';
import { Observable, of as observableOf } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Injectable()
export class BreadCrumbResolverService extends BaseService {

	private activeComponents = [];
	private parentComponents = [];
	private breadCrumbs: Observable<BreadcrumbItem[]> = observableOf([]);
	constructor(
		private router: Router,
		private zone: NgZone
	) { super(); }

	public push(component: any) {
		const existingComponentIndex = this.activeComponents.map(x => x.constructor.name).indexOf(component.constructor.name);
		if (existingComponentIndex !== -1) { this.activeComponents.splice(existingComponentIndex, 1); }
		this.activeComponents.push(component);
	}

	public clear() {
		this.activeComponents.length = 0;
		this.breadCrumbs = observableOf([]);
	}

	public resolve(activatedRoute: ActivatedRoute): Observable<BreadcrumbItem[]> {
		this.breadCrumbs = null;
		const routeComponents = this.getComponentsFromRoute(activatedRoute, []);
		this.activeComponents.filter(x => routeComponents.indexOf(x.constructor.name) !== -1).forEach(x => {
			if (x.hasOwnProperty('breadCrumbs')) {
				const componentItems = this.resolveDependentComponents((<IBreadCrumbComponent>x).breadCrumbs, []);
				this.breadCrumbs = observableOf(componentItems);
			}
		});
		return this.breadCrumbs;
	}

	private getComponentsFromRoute(activatedRoute: ActivatedRoute, routeComponents: any[]): any[] {
		activatedRoute.children.forEach(x => {
			if (x.children.length > 0) { this.getComponentsFromRoute(x.children[0], routeComponents); }
			if (x.component) { routeComponents.push(x.component['name']); }
		});
		if (activatedRoute.component) { routeComponents.push(activatedRoute.component['name']); }

		return routeComponents;
	}

	resolveDependentComponents(items: Observable<BreadcrumbItem[]>, components: any[]): any[] {
		items
			.pipe(takeUntil(this._destroyed))
			.subscribe(breadCrumbs => {
				breadCrumbs.forEach(async item => {
					const parentComponent = item.parentComponentName ? this.findComponent(item.parentComponentName) : null;
					if (parentComponent && parentComponent.hasOwnProperty('breadCrumbs')) {
						components = this.pushToStart(components, this.resolveDependentComponents((<IBreadCrumbComponent>parentComponent).breadCrumbs, components));
					} else if (item.notFoundResolver) {
						components = this.pushToStart(components, item.notFoundResolver);
						//components = this.pushToStart(components, [unresolvedComponentItems])
					}
				});
				components = this.pushToEnd(components, breadCrumbs);
			});
		return components;
	}

	private findComponent(componentName: string): any {
		for (let i = 0; i < this.activeComponents.length; i++) {
			if (this.activeComponents[i].constructor.name === componentName) { return this.activeComponents[i]; }
		}
		return null;
	}

	pushToStart(first: any[], second: any[]) {
		return [].concat(second.filter(x => first.map(firstX => firstX.label).indexOf(x.label) === -1), first);
	}

	pushToEnd(first: any[], second: any[]) {
		return [].concat(first, second.filter(x => first.map(firstX => firstX.label).indexOf(x.label) === -1));
	}
}
