/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import 'vs/css!./contextMenuHandler';
import { $, Builder } from 'vs/base/browser/builder';
import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IActionRunner, ActionRunner, IAction } from 'vs/base/common/actions';
import { Menu } from 'vs/base/browser/ui/menu/menu';
import { EventType } from 'vs/base/common/events';
import Severity from 'vs/base/common/severity';

import { IContextViewService, IContextMenuDelegate } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IMessageService } from 'vs/platform/message/common/message';

export class ContextMenuHandler {

	private contextViewService: IContextViewService;
	private messageService: IMessageService;
	private telemetryService: ITelemetryService;

	private actionRunner: IActionRunner;
	private $el: Builder;
	private menuContainerElement: HTMLElement;
	private toDispose: IDisposable[];

	constructor(element: HTMLElement, contextViewService: IContextViewService, telemetryService: ITelemetryService, messageService: IMessageService) {
		this.setContainer(element);

		this.contextViewService = contextViewService;
		this.telemetryService = telemetryService;
		this.messageService = messageService;

		this.actionRunner = new ActionRunner();
		this.menuContainerElement = null;
		this.toDispose = [];

		let hideViewOnRun = false;

		this.toDispose.push(this.actionRunner.addListener(EventType.BEFORE_RUN, (e: any) => {
			if (this.telemetryService) {
				/* __GDPR__
					"workbenchActionExecuted" : {
						"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
				this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: 'contextMenu' });
			}

			hideViewOnRun = !!e.retainActionItem;

			if (!hideViewOnRun) {
				this.contextViewService.hideContextView(false);
			}
		}));

		this.toDispose.push(this.actionRunner.addListener(EventType.RUN, (e: any) => {
			if (hideViewOnRun) {
				this.contextViewService.hideContextView(false);
			}

			hideViewOnRun = false;

			if (e.error && this.messageService) {
				this.messageService.show(Severity.Error, e.error);
			}
		}));
	}

	public setContainer(container: HTMLElement): void {
		if (this.$el) {
			this.$el.off(['click', 'mousedown']);
			this.$el = null;
		}
		if (container) {
			this.$el = $(container);
			this.$el.on('mousedown', (e: MouseEvent) => this.onMouseDown(e));
		}
	}

	public showContextMenu(delegate: IContextMenuDelegate): void {
		delegate.getActions().done((actions: IAction[]) => {
			this.contextViewService.showContextView({
				getAnchor: () => delegate.getAnchor(),
				canRelayout: false,

				render: (container) => {
					this.menuContainerElement = container;

					let className = delegate.getMenuClassName ? delegate.getMenuClassName() : '';

					if (className) {
						container.className += ' ' + className;
					}

					let menu = new Menu(container, actions, {
						actionItemProvider: delegate.getActionItem,
						context: delegate.getActionsContext ? delegate.getActionsContext() : null,
						actionRunner: this.actionRunner
					});

					let listener1 = menu.addListener(EventType.CANCEL, (e: any) => {
						this.contextViewService.hideContextView(true);
					});

					let listener2 = menu.addListener(EventType.BLUR, (e: any) => {
						this.contextViewService.hideContextView(true);
					});

					menu.focus();

					return combinedDisposable([listener1, listener2, menu]);
				},

				onHide: (didCancel?: boolean) => {
					if (delegate.onHide) {
						delegate.onHide(didCancel);
					}

					this.menuContainerElement = null;
				}
			});
		});
	}

	private onMouseDown(e: MouseEvent): void {
		if (!this.menuContainerElement) {
			return;
		}

		let event = new StandardMouseEvent(e);
		let element = event.target;

		while (element) {
			if (element === this.menuContainerElement) {
				return;
			}

			element = element.parentElement;
		}

		this.contextViewService.hideContextView();
	}

	public dispose(): void {
		this.setContainer(null);
	}
}