"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtHostTasks = void 0;
exports.createTaskApiFactory = createTaskApiFactory;
const tslib_1 = require("tslib");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const vscode_1 = require("../../../../common/vscode");
const types = tslib_1.__importStar(require("../../../../common/vscode/ext-types"));
const tasks_1 = require("../../../../common/vscode/tasks");
const ext_host_terminal_1 = require("../ext.host.terminal");
const taskTypes_1 = require("./taskTypes");
var TaskDefinitionDTO;
(function (TaskDefinitionDTO) {
    function from(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    TaskDefinitionDTO.from = from;
    function to(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    TaskDefinitionDTO.to = to;
})(TaskDefinitionDTO || (TaskDefinitionDTO = {}));
var TaskPresentationOptionsDTO;
(function (TaskPresentationOptionsDTO) {
    function from(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    TaskPresentationOptionsDTO.from = from;
    function to(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    TaskPresentationOptionsDTO.to = to;
})(TaskPresentationOptionsDTO || (TaskPresentationOptionsDTO = {}));
var ProcessExecutionOptionsDTO;
(function (ProcessExecutionOptionsDTO) {
    function from(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    ProcessExecutionOptionsDTO.from = from;
    function to(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    ProcessExecutionOptionsDTO.to = to;
})(ProcessExecutionOptionsDTO || (ProcessExecutionOptionsDTO = {}));
var ProcessExecutionDTO;
(function (ProcessExecutionDTO) {
    function is(value) {
        if (value) {
            const candidate = value;
            return candidate && !!candidate.process;
        }
        else {
            return false;
        }
    }
    ProcessExecutionDTO.is = is;
    function from(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        const result = {
            process: value.process,
            args: value.args,
        };
        if (value.options) {
            result.options = ProcessExecutionOptionsDTO.from(value.options);
        }
        return result;
    }
    ProcessExecutionDTO.from = from;
    function to(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return new types.ProcessExecution(value.process, value.args, value.options);
    }
    ProcessExecutionDTO.to = to;
})(ProcessExecutionDTO || (ProcessExecutionDTO = {}));
var ShellExecutionOptionsDTO;
(function (ShellExecutionOptionsDTO) {
    function from(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    ShellExecutionOptionsDTO.from = from;
    function to(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        return value;
    }
    ShellExecutionOptionsDTO.to = to;
})(ShellExecutionOptionsDTO || (ShellExecutionOptionsDTO = {}));
var ShellExecutionDTO;
(function (ShellExecutionDTO) {
    function is(value) {
        if (value) {
            const candidate = value;
            return candidate && (!!candidate.commandLine || !!candidate.command);
        }
        else {
            return false;
        }
    }
    ShellExecutionDTO.is = is;
    function from(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        const result = {};
        if (value.commandLine !== undefined) {
            result.commandLine = value.commandLine;
        }
        else {
            result.command = value.command;
            result.args = value.args;
        }
        if (value.options) {
            result.options = ShellExecutionOptionsDTO.from(value.options);
        }
        return result;
    }
    ShellExecutionDTO.from = from;
    function to(value) {
        if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) {
            return undefined;
        }
        if (value.commandLine) {
            return new types.ShellExecution(value.commandLine, value.options);
        }
        else {
            return new types.ShellExecution(value.command, value.args ? value.args : [], value.options);
        }
    }
    ShellExecutionDTO.to = to;
})(ShellExecutionDTO || (ShellExecutionDTO = {}));
var CustomExecutionDTO;
(function (CustomExecutionDTO) {
    function is(value) {
        if (value) {
            const candidate = value;
            return candidate && candidate.customExecution === 'customExecution';
        }
        else {
            return false;
        }
    }
    CustomExecutionDTO.is = is;
    function from(value) {
        return {
            customExecution: 'customExecution',
        };
    }
    CustomExecutionDTO.from = from;
    function to(taskId, providedCustomExecutions) {
        return providedCustomExecutions.get(taskId);
    }
    CustomExecutionDTO.to = to;
})(CustomExecutionDTO || (CustomExecutionDTO = {}));
var TaskHandleDTO;
(function (TaskHandleDTO) {
    function from(value) {
        let folder;
        if (value.scope !== undefined && typeof value.scope !== 'number') {
            folder = value.scope.uri;
        }
        return {
            id: value._id,
            workspaceFolder: folder,
        };
    }
    TaskHandleDTO.from = from;
})(TaskHandleDTO || (TaskHandleDTO = {}));
var CustomExecution2DTO;
(function (CustomExecution2DTO) {
    function is(value) {
        if (value) {
            const candidate = value;
            return candidate && candidate.customExecution === 'customExecution2';
        }
        else {
            return false;
        }
    }
    CustomExecution2DTO.is = is;
    function from(value) {
        return {
            customExecution: 'customExecution2',
        };
    }
    CustomExecution2DTO.from = from;
})(CustomExecution2DTO || (CustomExecution2DTO = {}));
var TaskDTO;
(function (TaskDTO) {
    function fromMany(tasks, extension) {
        if (tasks === undefined || tasks === null) {
            return [];
        }
        const result = [];
        for (const task of tasks) {
            const converted = from(task, extension);
            if (converted) {
                result.push(converted);
            }
        }
        return result;
    }
    TaskDTO.fromMany = fromMany;
    function from(value, extension) {
        if (value === undefined || value === null) {
            return undefined;
        }
        let execution;
        if (value.execution instanceof types.ProcessExecution) {
            execution = ProcessExecutionDTO.from(value.execution);
        }
        else if (value.execution instanceof types.ShellExecution) {
            execution = ShellExecutionDTO.from(value.execution);
        }
        else if (value.execution && value.execution instanceof types.CustomExecution) {
            execution = CustomExecutionDTO.from(value.execution);
        }
        const definition = TaskDefinitionDTO.from(value.definition);
        let scope;
        if (value.scope) {
            if (typeof value.scope === 'number') {
                scope = value.scope;
            }
            else {
                scope = value.scope.uri;
            }
        }
        else {
            // To continue to support the deprecated task constructor that doesn't take a scope, we must add a scope here:
            scope = types.TaskScope.Workspace;
        }
        if (!definition || !scope) {
            return undefined;
        }
        const group = value.group ? value.group.id : undefined;
        const result = {
            _id: value._id,
            definition,
            name: value.name,
            source: {
                extensionId: extension.id,
                label: value.source,
                scope,
            },
            execution: execution,
            isBackground: value.isBackground,
            group,
            presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
            problemMatchers: value.problemMatchers,
            hasDefinedMatchers: value.hasDefinedMatchers,
            runOptions: value.runOptions ? value.runOptions : { reevaluateOnRerun: true },
        };
        return result;
    }
    TaskDTO.from = from;
    async function to(value, extHostWorkspace, providedCustomExecutions) {
        if (value === undefined || value === null) {
            return undefined;
        }
        let execution;
        if (ProcessExecutionDTO.is(value.execution)) {
            execution = ProcessExecutionDTO.to(value.execution);
        }
        else if (ShellExecutionDTO.is(value.execution)) {
            execution = ShellExecutionDTO.to(value.execution);
        }
        else if (CustomExecutionDTO.is(value.execution)) {
            execution = CustomExecutionDTO.to(value._id, providedCustomExecutions);
        }
        const definition = TaskDefinitionDTO.to(value.definition);
        let scope;
        if (value.source) {
            if (value.source.scope !== undefined) {
                if (typeof value.source.scope === 'number') {
                    scope = value.source.scope;
                }
                else {
                    scope = extHostWorkspace.getWorkspaceFolder(ide_core_common_1.Uri.from(value.source.scope));
                }
            }
            else {
                scope = types.TaskScope.Workspace;
            }
        }
        if (!definition || !scope) {
            return undefined;
        }
        const result = new types.Task(definition, scope, value.name, value.source.label, execution, value.problemMatchers);
        if (value.isBackground !== undefined) {
            result.isBackground = value.isBackground;
        }
        if (value.group !== undefined) {
            result.group = types.TaskGroup.from(value.group);
        }
        if (value.presentationOptions) {
            result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions);
        }
        if (value._id) {
            result._id = value._id;
        }
        return result;
    }
    TaskDTO.to = to;
})(TaskDTO || (TaskDTO = {}));
/**
 * VS Code 似乎不打算实现？
 */
class TaskExecutionImpl {
    constructor(_tasks, _id, _task) {
        this._tasks = _tasks;
        this._id = _id;
        this._task = _task;
    }
    get task() {
        return this._task;
    }
    terminate() {
        this._tasks.terminateTask(this);
    }
    fireDidStartProcess(value) { }
    fireDidEndProcess(value) { }
}
class CustomExecutionData {
    constructor(customExecution, terminalService) {
        this.customExecution = customExecution;
        this.terminalService = terminalService;
        this._onTaskExecutionComplete = new ide_core_common_1.Emitter();
        this._disposables = new ide_core_common_1.DisposableStore();
    }
    dispose() {
        this._cancellationSource = undefined;
        this._disposables.dispose();
    }
    get onTaskExecutionComplete() {
        return this._onTaskExecutionComplete.event;
    }
    onDidOpenTerminal(terminal) {
        if (!(terminal instanceof ext_host_terminal_1.Terminal)) {
            throw new Error('How could this not be a extension host terminal?');
        }
        if (this.terminalId && terminal.__id === String(this.terminalId)) {
            this.startCallback(this.terminalId);
        }
    }
    async startCallback(terminalId) {
        this.terminalId = terminalId;
        // If we have already started the extension task callback, then
        // do not start it again.
        // It is completely valid for multiple terminals to be opened
        // before the one for our task.
        if (this._cancellationSource) {
            return undefined;
        }
        const callbackTerminals = this.terminalService.terminals.filter(
        // @ts-ignore
        (terminal) => terminal._id === terminalId);
        if (!callbackTerminals || callbackTerminals.length === 0) {
            this._disposables.add(this.terminalService.onDidOpenTerminal(this.onDidOpenTerminal.bind(this)));
            return;
        }
        if (callbackTerminals.length !== 1) {
            throw new Error('Expected to only have one terminal at this point');
        }
    }
}
class ExtHostTasks {
    constructor(rpcProtocol, terminalService, extHostWorkspace) {
        this.rpcProtocol = rpcProtocol;
        this.terminalService = terminalService;
        this.extHostWorkspace = extHostWorkspace;
        this.handlerCounter = 0;
        this.taskHandlers = new Map();
        this._onDidExecuteTask = new ide_core_common_1.Emitter();
        this._onDidTerminateTask = new ide_core_common_1.Emitter();
        this._onDidTaskProcessStarted = new ide_core_common_1.Emitter();
        this._onDidTaskProcessEnded = new ide_core_common_1.Emitter();
        this.proxy = this.rpcProtocol.getProxy(vscode_1.MainThreadAPIIdentifier.MainThreadTasks);
        this.providedCustomExecutions2 = new Map();
        this.notProvidedCustomExecutions = new Set();
        this._taskExecutions = new Map();
    }
    async $onDidStartTask(execution, terminalId) {
        var _a;
        const customExecution = this.providedCustomExecutions2.get(execution.id);
        if (customExecution) {
            this.terminalService.attachPtyToTerminal(terminalId, await customExecution.callback((_a = execution.task) === null || _a === void 0 ? void 0 : _a.definition));
        }
        this._onDidExecuteTask.fire({
            execution: await this.getTaskExecution(execution),
        });
    }
    get onDidStartTask() {
        return this._onDidExecuteTask.event;
    }
    async $onDidEndTask(execution) {
        this._onDidTerminateTask.fire({
            execution: await this.getTaskExecution(execution),
        });
    }
    get onDidEndTask() {
        return this._onDidTerminateTask.event;
    }
    async $onDidStartTaskProcess(value) {
        const execution = await this.getTaskExecution(value.id);
        if (execution) {
            this._onDidTaskProcessStarted.fire({
                execution,
                processId: value.processId,
            });
        }
    }
    get onDidStartTaskProcess() {
        return this._onDidTaskProcessStarted.event;
    }
    async $onDidEndTaskProcess(value) {
        const execution = await this.getTaskExecution(value.id);
        if (execution) {
            this._onDidTaskProcessEnded.fire({
                execution,
                exitCode: value.exitCode,
            });
        }
    }
    get onDidEndTaskProcess() {
        return this._onDidTaskProcessEnded.event;
    }
    get taskExecutions() {
        return [...this._taskExecutions.values()];
    }
    terminateTask(execution) {
        return this.proxy.$terminateTask(execution._id);
    }
    registerTaskProvider(type, provider, extension) {
        const handler = (this.handlerCounter += 1);
        this.taskHandlers.set(handler, { type, provider, extension });
        this.proxy.$registerTaskProvider(handler, type);
        return {
            dispose: () => {
                this.taskHandlers.delete(handler);
                this.proxy.$unregisterTaskProvider(type);
            },
        };
    }
    async getTaskExecution(execution, task) {
        if (typeof execution === 'string') {
            const taskExecution = this._taskExecutions.get(execution);
            if (!taskExecution) {
                throw new Error(`Unexpected: The specified task is missing an execution : ${execution}`);
            }
            return taskExecution;
        }
        const result = this._taskExecutions.get(execution.id);
        if (result) {
            return result;
        }
        const taskToCreate = task
            ? task
            : await TaskDTO.to(execution.task, this.extHostWorkspace, this.providedCustomExecutions2);
        if (!taskToCreate) {
            throw new Error('Unexpected: Task does not exist.');
        }
        const createdResult = new TaskExecutionImpl(this, execution.id, taskToCreate);
        this._taskExecutions.set(execution.id, createdResult);
        return createdResult;
    }
    async executeTask(task, extension) {
        const tTask = task;
        // We have a preserved ID. So the task didn't change.
        if (tTask._id !== undefined) {
            return this.proxy.$executeTask(TaskHandleDTO.from(tTask)).then((value) => this.getTaskExecution(value, task));
        }
        else {
            const dto = TaskDTO.from(task, extension);
            if (dto === undefined) {
                return Promise.reject(new Error('Task is not valid'));
            }
            // If this task is a custom execution, then we need to save it away
            // in the provided custom execution map that is cleaned up after the
            // task is executed.
            if (CustomExecutionDTO.is(dto.execution)) {
                await this.addCustomExecution(dto, task, false);
            }
            return this.proxy.$executeTask(dto).then((value) => this.getTaskExecution(value, task));
        }
    }
    fetchTasks(filter) {
        return this.proxy.$fetchTasks(tasks_1.TaskFilterDTO.from(filter)).then(async (values) => {
            const result = [];
            for (const value of values) {
                const task = await TaskDTO.to(value, this.extHostWorkspace, this.providedCustomExecutions2);
                if (task) {
                    result.push(task);
                }
            }
            return result;
        });
    }
    async addCustomExecution(taskDTO, task, isProvided) {
        const taskId = await this.proxy.$createTaskId(taskDTO);
        if (!isProvided && !this.providedCustomExecutions2.has(taskId)) {
            this.notProvidedCustomExecutions.add(taskId);
        }
        this.providedCustomExecutions2.set(taskId, task.execution);
    }
    $provideTask(handler, validTypes) {
        const provider = this.taskHandlers.get(handler);
        if (!provider) {
            throw new Error(`taskprovider ${handler} not found`);
        }
        const taskIdPromises = [];
        const fetching = (0, ide_core_common_1.asPromise)(() => provider.provider.provideTasks(ide_core_common_1.CancellationToken.None))
            .then((result) => {
            const taskDTOs = [];
            if (result) {
                for (const task of result) {
                    if (!task.definition || !validTypes[task.definition.type]) {
                        (0, ide_core_common_1.getDebugLogger)().warn(false, `The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
                    }
                    const taskDTO = TaskDTO.from(task, provider.extension);
                    if (taskDTO) {
                        taskDTOs.push(taskDTO);
                        if (CustomExecutionDTO.is(taskDTO.execution)) {
                            // The ID is calculated on the main thread task side, so, let's call into it here.
                            // We need the task id's pre-computed for custom task executions because when OnDidStartTask
                            // is invoked, we have to be able to map it back to our data.
                            taskIdPromises.push(this.addCustomExecution(taskDTO, task, false));
                        }
                    }
                }
            }
            return {
                tasks: taskDTOs,
                extension: provider.extension,
            };
        })
            .catch((err) => {
            (0, ide_core_common_1.getDebugLogger)().error(err, provider.extension);
            return {
                tasks: [],
                extension: provider.extension,
            };
        });
        return new Promise((resolve) => {
            fetching.then((result) => {
                Promise.all(taskIdPromises).then(() => {
                    resolve(result);
                });
            });
        });
    }
    async $resolveTask(handle, taskDTO) {
        const taskDto = (0, taskTypes_1.toTask)(taskDTO);
        const handler = this.taskHandlers.get(handle);
        if (!handler) {
            return Promise.reject(new Error('no handler found!'));
        }
        const resolvedTask = await handler.provider.resolveTask(taskDto, ide_core_common_1.CancellationToken.None);
        if (!resolvedTask) {
            return;
        }
        const resolvedTaskDTO = TaskDTO.from(resolvedTask, handler.extension);
        if (!resolvedTaskDTO) {
            throw new Error('Unexpected: Task cannot be resolved.');
        }
        if (CustomExecutionDTO.is(resolvedTaskDTO.execution)) {
            await this.addCustomExecution(resolvedTaskDTO, resolvedTask, true);
        }
        return resolvedTaskDTO;
    }
}
exports.ExtHostTasks = ExtHostTasks;
function createTaskApiFactory(extHostTasks, extension) {
    return {
        registerTaskProvider: (type, provider) => extHostTasks.registerTaskProvider(type, provider, extension),
        fetchTasks(filter) {
            return extHostTasks.fetchTasks(filter);
        },
        executeTask: (task) => extHostTasks.executeTask(task, extension),
        get taskExecutions() {
            return extHostTasks.taskExecutions;
        },
        onDidStartTask: (listener, thisArg, disposables) => extHostTasks.onDidStartTask(listener, thisArg, disposables),
        onDidEndTask(listener, thisArg, disposables) {
            return extHostTasks.onDidEndTask(listener, thisArg, disposables);
        },
        onDidStartTaskProcess(listener, thisArg, disposables) {
            return extHostTasks.onDidStartTaskProcess(listener, thisArg, disposables);
        },
        onDidEndTaskProcess(listener, thisArg, disposables) {
            return extHostTasks.onDidEndTaskProcess(listener, thisArg, disposables);
        },
    };
}
//# sourceMappingURL=ext.host.tasks.js.map