import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { assertFn } from '../../../../base/common/assert.js';
import { BugIndicatingError } from '../../../../base/common/errors.js';
import { Event } from '../../../../base/common/event.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
import { observableValue } from '../../../../base/common/observableInternal/base.js';
import { derived } from '../../../../base/common/observableInternal/derived.js';
import '../../../../base/common/observableInternal/autorun.js';
import { observableFromEvent } from '../../../../base/common/observableInternal/utils.js';
import '../../../../base/common/cancellation.js';
import '../../../../base/common/arrays.js';
import { basename, isEqual } from '../../../../base/common/resources.js';
import Severity$1 from '../../../../base/common/severity.js';
import { IModelService } from '../../../../editor/common/services/model.js';
import { ITextModelService } from '../../../../editor/common/services/resolverService.js';
import { localizeWithPath } from '../../../../nls.js';
import { ConfirmResult } from '../../../../platform/dialogs/common/dialogs.js';
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.service.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { IStorageService } from '../../../../platform/storage/common/storage.service.js';
import { SaveSourceRegistry } from '../../../common/editor.js';
import { EditorModel } from '../../../common/editor/editorModel.js';
import { conflictMarkers } from './mergeMarkers/mergeMarkersController.js';
import { MergeDiffComputer } from './model/diffComputer.js';
import { MergeEditorModel } from './model/mergeEditorModel.js';
import { StorageCloseWithConflicts } from '../common/mergeEditor.js';
import { IEditorService } from '../../../services/editor/common/editorService.service.js';
import { ITextFileService } from '../../../services/textfile/common/textfiles.service.js';
var WorkspaceMergeEditorModeFactory_1;
const _moduleId = "vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel";
let TempFileMergeEditorModeFactory = class TempFileMergeEditorModeFactory {
    constructor(_mergeEditorTelemetry, _instantiationService, _textModelService, _modelService) {
        this._mergeEditorTelemetry = _mergeEditorTelemetry;
        this._instantiationService = _instantiationService;
        this._textModelService = _textModelService;
        this._modelService = _modelService;
    }
    async createInputModel(args) {
        const store = ( (new DisposableStore()));
        const [base, result, input1Data, input2Data,] = await Promise.all([
            this._textModelService.createModelReference(args.base),
            this._textModelService.createModelReference(args.result),
            toInputData(args.input1, this._textModelService, store),
            toInputData(args.input2, this._textModelService, store),
        ]);
        store.add(base);
        store.add(result);
        const tempResultUri = result.object.textEditorModel.uri.with({ scheme: 'merge-result' });
        const temporaryResultModel = this._modelService.createModel('', {
            languageId: result.object.textEditorModel.getLanguageId(),
            onDidChange: Event.None,
        }, tempResultUri);
        store.add(temporaryResultModel);
        const mergeDiffComputer = this._instantiationService.createInstance(MergeDiffComputer);
        const model = this._instantiationService.createInstance(MergeEditorModel, base.object.textEditorModel, input1Data, input2Data, temporaryResultModel, mergeDiffComputer, {
            resetResult: true,
        }, this._mergeEditorTelemetry);
        store.add(model);
        await model.onInitialized;
        return this._instantiationService.createInstance(TempFileMergeEditorInputModel, model, store, result.object, args.result);
    }
};
TempFileMergeEditorModeFactory = ( (__decorate([
    ( (__param(1, IInstantiationService))),
    ( (__param(2, ITextModelService))),
    ( (__param(3, IModelService)))
], TempFileMergeEditorModeFactory)));
let TempFileMergeEditorInputModel = class TempFileMergeEditorInputModel extends EditorModel {
    constructor(model, disposable, result, resultUri, textFileService, dialogService, editorService) {
        super();
        this.model = model;
        this.disposable = disposable;
        this.result = result;
        this.resultUri = resultUri;
        this.textFileService = textFileService;
        this.dialogService = dialogService;
        this.editorService = editorService;
        this.savedAltVersionId = observableValue(this, this.model.resultTextModel.getAlternativeVersionId());
        this.altVersionId = observableFromEvent(this, e => this.model.resultTextModel.onDidChangeContent(e), () =>
         this.model.resultTextModel.getAlternativeVersionId());
        this.isDirty = derived(this, (reader) => this.altVersionId.read(reader) !== this.savedAltVersionId.read(reader));
        this.finished = false;
    }
    dispose() {
        this.disposable.dispose();
        super.dispose();
    }
    async accept() {
        const value = await this.model.resultTextModel.getValue();
        this.result.textEditorModel.setValue(value);
        this.savedAltVersionId.set(this.model.resultTextModel.getAlternativeVersionId(), undefined);
        await this.textFileService.save(this.result.textEditorModel.uri);
        this.finished = true;
    }
    async _discard() {
        await this.textFileService.revert(this.model.resultTextModel.uri);
        this.savedAltVersionId.set(this.model.resultTextModel.getAlternativeVersionId(), undefined);
        this.finished = true;
    }
    shouldConfirmClose() {
        return true;
    }
    async confirmClose(inputModels) {
        assertFn(() => ( (inputModels.some((m) => m === this))));
        const someDirty = ( (inputModels.some((m) => m.isDirty.get())));
        let choice;
        if (someDirty) {
            const isMany = inputModels.length > 1;
            const message = isMany
                ? ( localizeWithPath(
                _moduleId,
                0,
                'Do you want keep the merge result of {0} files?',
                inputModels.length
            ))
                : ( localizeWithPath(
                _moduleId,
                1,
                'Do you want keep the merge result of {0}?',
                basename(inputModels[0].model.resultTextModel.uri)
            ));
            const hasUnhandledConflicts = ( (inputModels.some((m) => m.model.hasUnhandledConflicts.get())));
            const buttons = [
                {
                    label: hasUnhandledConflicts ?
                        ( localizeWithPath(_moduleId, 2, "&&Save With Conflicts")) :
                        ( localizeWithPath(_moduleId, 3, "&&Save")),
                    run: () => ConfirmResult.SAVE
                },
                {
                    label: ( localizeWithPath(_moduleId, 4, "Do&&n't Save")),
                    run: () => ConfirmResult.DONT_SAVE
                }
            ];
            choice = (await this.dialogService.prompt({
                type: Severity$1.Info,
                message,
                detail: hasUnhandledConflicts
                    ? isMany
                        ? ( localizeWithPath(
                    _moduleId,
                    5,
                    "The files contain unhandled conflicts. The merge results will be lost if you don't save them."
                ))
                        : ( localizeWithPath(
                    _moduleId,
                    6,
                    "The file contains unhandled conflicts. The merge result will be lost if you don't save it."
                ))
                    : isMany
                        ? ( localizeWithPath(_moduleId, 7, "The merge results will be lost if you don't save them."))
                        : ( localizeWithPath(_moduleId, 8, "The merge result will be lost if you don't save it.")),
                buttons,
                cancelButton: {
                    run: () => ConfirmResult.CANCEL
                }
            })).result;
        }
        else {
            choice = ConfirmResult.DONT_SAVE;
        }
        if (choice === ConfirmResult.SAVE) {
            await Promise.all(( (inputModels.map(m => m.accept()))));
        }
        else if (choice === ConfirmResult.DONT_SAVE) {
            await Promise.all(( (inputModels.map(m => m._discard()))));
        }
        else ;
        return choice;
    }
    async save(options) {
        if (this.finished) {
            return;
        }
        (async () => {
            const { confirmed } = await this.dialogService.confirm({
                message: ( localizeWithPath(_moduleId, 9, "Do you want to accept the merge result?")),
                detail: ( localizeWithPath(
                    _moduleId,
                    10,
                    "This will write the merge result to the original file and close the merge editor."
                )),
                primaryButton: ( localizeWithPath(_moduleId, 11, '&&Accept Merge'))
            });
            if (confirmed) {
                await this.accept();
                const editors = this.editorService.findEditors(this.resultUri).filter(e => e.editor.typeId === 'mergeEditor.Input');
                await this.editorService.closeEditors(editors);
            }
        })();
    }
    async revert(options) {
    }
};
TempFileMergeEditorInputModel = ( (__decorate([
    ( (__param(4, ITextFileService))),
    ( (__param(5, IDialogService))),
    ( (__param(6, IEditorService)))
], TempFileMergeEditorInputModel)));
let WorkspaceMergeEditorModeFactory = class WorkspaceMergeEditorModeFactory {
    static { WorkspaceMergeEditorModeFactory_1 = this; }
    constructor(_mergeEditorTelemetry, _instantiationService, _textModelService, textFileService) {
        this._mergeEditorTelemetry = _mergeEditorTelemetry;
        this._instantiationService = _instantiationService;
        this._textModelService = _textModelService;
        this.textFileService = textFileService;
    }
    static { this.FILE_SAVED_SOURCE = SaveSourceRegistry.registerSource('merge-editor.source', ( localizeWithPath(_moduleId, 12, "Before Resolving Conflicts In Merge Editor"))); }
    async createInputModel(args) {
        const store = ( (new DisposableStore()));
        let resultTextFileModel = undefined;
        const modelListener = store.add(( (new DisposableStore())));
        const handleDidCreate = (model) => {
            if (isEqual(args.result, model.resource)) {
                modelListener.clear();
                resultTextFileModel = model;
            }
        };
        modelListener.add(this.textFileService.files.onDidCreate(handleDidCreate));
        this.textFileService.files.models.forEach(handleDidCreate);
        const [base, result, input1Data, input2Data,] = await Promise.all([
            this._textModelService.createModelReference(args.base),
            this._textModelService.createModelReference(args.result),
            toInputData(args.input1, this._textModelService, store),
            toInputData(args.input2, this._textModelService, store),
        ]);
        store.add(base);
        store.add(result);
        if (!resultTextFileModel) {
            throw ( (new BugIndicatingError()));
        }
        await resultTextFileModel.save({ source: WorkspaceMergeEditorModeFactory_1.FILE_SAVED_SOURCE });
        const lines = resultTextFileModel.textEditorModel.getLinesContent();
        const hasConflictMarkers = ( (lines.some(l => l.startsWith(conflictMarkers.start))));
        const resetResult = hasConflictMarkers;
        const mergeDiffComputer = this._instantiationService.createInstance(MergeDiffComputer);
        const model = this._instantiationService.createInstance(MergeEditorModel, base.object.textEditorModel, input1Data, input2Data, result.object.textEditorModel, mergeDiffComputer, {
            resetResult
        }, this._mergeEditorTelemetry);
        store.add(model);
        await model.onInitialized;
        return this._instantiationService.createInstance(WorkspaceMergeEditorInputModel, model, store, resultTextFileModel, this._mergeEditorTelemetry);
    }
};
WorkspaceMergeEditorModeFactory = WorkspaceMergeEditorModeFactory_1 = ( (__decorate([
    ( (__param(1, IInstantiationService))),
    ( (__param(2, ITextModelService))),
    ( (__param(3, ITextFileService)))
], WorkspaceMergeEditorModeFactory)));
let WorkspaceMergeEditorInputModel = class WorkspaceMergeEditorInputModel extends EditorModel {
    constructor(model, disposableStore, resultTextFileModel, telemetry, _dialogService, _storageService) {
        super();
        this.model = model;
        this.disposableStore = disposableStore;
        this.resultTextFileModel = resultTextFileModel;
        this.telemetry = telemetry;
        this._dialogService = _dialogService;
        this._storageService = _storageService;
        this.isDirty = observableFromEvent(this, Event.any(this.resultTextFileModel.onDidChangeDirty, this.resultTextFileModel.onDidSaveError), () =>  this.resultTextFileModel.isDirty());
        this.reported = false;
        this.dateTimeOpened = ( (new Date()));
    }
    dispose() {
        this.disposableStore.dispose();
        super.dispose();
        this.reportClose(false);
    }
    reportClose(accepted) {
        if (!this.reported) {
            const remainingConflictCount = this.model.unhandledConflictsCount.get();
            const durationOpenedMs = ( (new Date())).getTime() - this.dateTimeOpened.getTime();
            this.telemetry.reportMergeEditorClosed({
                durationOpenedSecs: durationOpenedMs / 1000,
                remainingConflictCount,
                accepted,
                conflictCount: this.model.conflictCount,
                combinableConflictCount: this.model.combinableConflictCount,
                conflictsResolvedWithBase: this.model.conflictsResolvedWithBase,
                conflictsResolvedWithInput1: this.model.conflictsResolvedWithInput1,
                conflictsResolvedWithInput2: this.model.conflictsResolvedWithInput2,
                conflictsResolvedWithSmartCombination: this.model.conflictsResolvedWithSmartCombination,
                manuallySolvedConflictCountThatEqualNone: this.model.manuallySolvedConflictCountThatEqualNone,
                manuallySolvedConflictCountThatEqualSmartCombine: this.model.manuallySolvedConflictCountThatEqualSmartCombine,
                manuallySolvedConflictCountThatEqualInput1: this.model.manuallySolvedConflictCountThatEqualInput1,
                manuallySolvedConflictCountThatEqualInput2: this.model.manuallySolvedConflictCountThatEqualInput2,
                manuallySolvedConflictCountThatEqualNoneAndStartedWithBase: this.model.manuallySolvedConflictCountThatEqualNoneAndStartedWithBase,
                manuallySolvedConflictCountThatEqualNoneAndStartedWithInput1: this.model.manuallySolvedConflictCountThatEqualNoneAndStartedWithInput1,
                manuallySolvedConflictCountThatEqualNoneAndStartedWithInput2: this.model.manuallySolvedConflictCountThatEqualNoneAndStartedWithInput2,
                manuallySolvedConflictCountThatEqualNoneAndStartedWithBothNonSmart: this.model.manuallySolvedConflictCountThatEqualNoneAndStartedWithBothNonSmart,
                manuallySolvedConflictCountThatEqualNoneAndStartedWithBothSmart: this.model.manuallySolvedConflictCountThatEqualNoneAndStartedWithBothSmart,
            });
            this.reported = true;
        }
    }
    async accept() {
        this.reportClose(true);
        await this.resultTextFileModel.save();
    }
    get resultUri() {
        return this.resultTextFileModel.resource;
    }
    async save(options) {
        await this.resultTextFileModel.save(options);
    }
    async revert(options) {
        await this.resultTextFileModel.revert(options);
    }
    shouldConfirmClose() {
        return true;
    }
    async confirmClose(inputModels) {
        const isMany = inputModels.length > 1;
        const someDirty = ( (inputModels.some(m => m.isDirty.get())));
        const someUnhandledConflicts = ( (inputModels.some(m => m.model.hasUnhandledConflicts.get())));
        if (someDirty) {
            const message = isMany
                ? ( localizeWithPath(
                _moduleId,
                13,
                'Do you want to save the changes you made to {0} files?',
                inputModels.length
            ))
                : ( localizeWithPath(
                _moduleId,
                14,
                'Do you want to save the changes you made to {0}?',
                basename(inputModels[0].resultUri)
            ));
            const { result } = await this._dialogService.prompt({
                type: Severity$1.Info,
                message,
                detail: someUnhandledConflicts ?
                    isMany
                        ? ( localizeWithPath(
                        _moduleId,
                        15,
                        "The files contain unhandled conflicts. Your changes will be lost if you don't save them."
                    ))
                        : ( localizeWithPath(
                        _moduleId,
                        16,
                        "The file contains unhandled conflicts. Your changes will be lost if you don't save them."
                    ))
                    : isMany
                        ? ( localizeWithPath(_moduleId, 17, "Your changes will be lost if you don't save them."))
                        : ( localizeWithPath(_moduleId, 18, "Your changes will be lost if you don't save them.")),
                buttons: [
                    {
                        label: someUnhandledConflicts
                            ? ( localizeWithPath(_moduleId, 19, '&&Save with Conflicts'))
                            : ( localizeWithPath(_moduleId, 20, '&&Save')),
                        run: () => ConfirmResult.SAVE
                    },
                    {
                        label: ( localizeWithPath(_moduleId, 21, "Do&&n't Save")),
                        run: () => ConfirmResult.DONT_SAVE
                    }
                ],
                cancelButton: {
                    run: () => ConfirmResult.CANCEL
                }
            });
            return result;
        }
        else if (someUnhandledConflicts && !this._storageService.getBoolean(StorageCloseWithConflicts, StorageScope.PROFILE, false)) {
            const { confirmed, checkboxChecked } = await this._dialogService.confirm({
                message: isMany
                    ? ( localizeWithPath(
                    _moduleId,
                    22,
                    'Do you want to close {0} merge editors?',
                    inputModels.length
                ))
                    : ( localizeWithPath(
                    _moduleId,
                    23,
                    'Do you want to close the merge editor for {0}?',
                    basename(inputModels[0].resultUri)
                )),
                detail: someUnhandledConflicts ?
                    isMany
                        ? ( localizeWithPath(_moduleId, 24, "The files contain unhandled conflicts."))
                        : ( localizeWithPath(_moduleId, 25, "The file contains unhandled conflicts."))
                    : undefined,
                primaryButton: someUnhandledConflicts
                    ? ( localizeWithPath(_moduleId, 26, '&&Close with Conflicts'))
                    : ( localizeWithPath(_moduleId, 27, '&&Close')),
                checkbox: { label: ( localizeWithPath(_moduleId, 28, "Do not ask me again")) }
            });
            if (checkboxChecked) {
                this._storageService.store(StorageCloseWithConflicts, true, StorageScope.PROFILE, StorageTarget.USER);
            }
            return confirmed ? ConfirmResult.SAVE : ConfirmResult.CANCEL;
        }
        else {
            return ConfirmResult.SAVE;
        }
    }
};
WorkspaceMergeEditorInputModel = ( (__decorate([
    ( (__param(4, IDialogService))),
    ( (__param(5, IStorageService)))
], WorkspaceMergeEditorInputModel)));
async function toInputData(data, textModelService, store) {
    const ref = await textModelService.createModelReference(data.uri);
    store.add(ref);
    return {
        textModel: ref.object.textEditorModel,
        title: data.title,
        description: data.description,
        detail: data.detail,
    };
}
export { TempFileMergeEditorModeFactory, WorkspaceMergeEditorModeFactory };
