// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { inject, injectable, optional } from '@theia/core/shared/inversify';
import {
    codiconArray,
    QuickAccessContribution,
    QuickAccessProvider,
    QuickAccessRegistry,
    QuickInputService
} from '@theia/core/lib/browser';
import { CancellationToken, CommandContribution, CommandRegistry, CommandService, nls } from '@theia/core/lib/common';
import { TerminalWidget } from './base/terminal-widget';
import { TerminalService } from './base/terminal-service';
import { TerminalCommands } from './terminal-frontend-contribution';
import { filterItems, QuickPickItem, QuickPicks } from '@theia/core/lib/browser/quick-input/quick-input-service';

@injectable()
export class TerminalQuickOpenService implements QuickAccessProvider {
    static readonly PREFIX = 'term ';

    @inject(QuickInputService) @optional()
    protected readonly quickInputService: QuickInputService;

    @inject(QuickAccessRegistry)
    protected readonly quickAccessRegistry: QuickAccessRegistry;

    @inject(CommandService)
    protected readonly commandService: CommandService;

    @inject(TerminalService)
    protected readonly terminalService: TerminalService;

    open(): void {
        this.quickInputService?.open(TerminalQuickOpenService.PREFIX);
    }

    async getPicks(filter: string, token: CancellationToken): Promise<QuickPicks> {
        const items: QuickPickItem[] = [];

        // Get the sorted list of currently opened terminal widgets that aren't hidden from users
        const widgets: TerminalWidget[] = this.terminalService.all.filter(widget => !widget.hiddenFromUser)
            .sort((a: TerminalWidget, b: TerminalWidget) => this.compareItems(a, b));

        for (const widget of widgets) {
            items.push(this.toItem(widget));
        }
        // Append a quick open item to create a new terminal.
        items.push({
            label: nls.localizeByDefault('Create New Terminal'),
            iconClasses: codiconArray('add'),
            execute: () => this.doCreateNewTerminal()
        });

        return filterItems(items, filter);
    }

    registerQuickAccessProvider(): void {
        this.quickAccessRegistry.registerQuickAccessProvider({
            getInstance: () => this,
            prefix: TerminalQuickOpenService.PREFIX,
            placeholder: '',
            helpEntries: [{ description: nls.localizeByDefault('Show All Opened Terminals'), needsEditor: false }]
        });
    }

    /**
     * Compare two terminal widgets by label. If labels are identical, compare by the widget id.
     * @param a `TerminalWidget` for comparison
     * @param b `TerminalWidget` for comparison
     */
    protected compareItems(a: TerminalWidget, b: TerminalWidget): number {
        const normalize = (str: string) => str.trim().toLowerCase();

        if (normalize(a.title.label) !== normalize(b.title.label)) {
            return normalize(a.title.label).localeCompare(normalize(b.title.label));
        } else {
            return normalize(a.id).localeCompare(normalize(b.id));
        }
    }

    protected doCreateNewTerminal(): void {
        this.commandService.executeCommand(TerminalCommands.NEW.id);
    }

    /**
     * Convert the terminal widget to the quick pick item.
     * @param {TerminalWidget} widget - the terminal widget.
     * @returns quick pick item.
     */
    protected toItem(widget: TerminalWidget): QuickPickItem {
        return {
            label: widget.title.label,
            description: widget.id,
            ariaLabel: widget.title.label,
            execute: () => this.terminalService.open(widget)
        };
    }
}

/**
 * TODO: merge it to TerminalFrontendContribution.
 */
@injectable()
export class TerminalQuickOpenContribution implements CommandContribution, QuickAccessContribution {

    @inject(TerminalQuickOpenService)
    protected readonly terminalQuickOpenService: TerminalQuickOpenService;

    registerQuickAccessProvider(): void {
        this.terminalQuickOpenService.registerQuickAccessProvider();
    }

    registerCommands(commands: CommandRegistry): void {
        commands.registerCommand(TerminalCommands.SHOW_ALL_OPENED_TERMINALS, {
            execute: () => this.terminalQuickOpenService.open()
        });
    }
}
