import {
	Directive, EventEmitter, Input, NgZone, OnChanges, OnDestroy, OnInit, Output,
	SimpleChange
} from '@angular/core';

import {Layer, LeafletEvent } from 'leaflet';

import { LeafletDirective } from '../core/leaflet.directive';
import { LeafletDirectiveWrapper } from '../core/leaflet.directive.wrapper';
import { LeafletUtil } from '../core/leaflet.util';


/**
 * Layer directive
 *
 * This directive is used to directly control a single map layer. The purpose of this directive is to
 * be used as part of a child structural directive of the map element.
 *
 */
@Directive({
	selector: '[leafletLayer]'
})
export class LeafletLayerDirective
	implements OnChanges, OnDestroy, OnInit {

	@Input('leafletLayer') layer: Layer;

	// Layer Events
	@Output('leafletLayerAdd') onAdd = new EventEmitter<LeafletEvent>();
	@Output('leafletLayerRemove') onRemove = new EventEmitter<LeafletEvent>();

	// Wrapper for the leaflet directive (manages the parent directive)
	private leafletDirective: LeafletDirectiveWrapper;

	constructor(leafletDirective: LeafletDirective, private zone: NgZone) {
		this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
	}

	ngOnInit() {

		// Init the map
		this.leafletDirective.init();

	}

	ngOnDestroy() {
		this.layer.remove();
	}

	ngOnChanges(changes: { [key: string]: SimpleChange }) {

		if (changes['layer']) {

			// Update the layer
			const p: Layer = changes['layer'].previousValue;
			const n = changes['layer'].currentValue;

			this.zone.runOutsideAngular(() => {
				if (null != p) {
					p.remove();
				}
				if (null != n) {
					this.addLayerEventListeners(n);
					this.leafletDirective.getMap().addLayer(n);
				}
			});

		}

	}

	private addLayerEventListeners(l: Layer) {

		l.on('add', (e: LeafletEvent) => LeafletUtil.handleEvent(this.zone, this.onAdd, e));
		l.on('remove', (e: LeafletEvent) => LeafletUtil.handleEvent(this.zone, this.onRemove, e));

	}

}