/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "../jsonLanguageTypes", "@vscode/l10n", "../utils/objects"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.JSONValidation = void 0;
    const jsonLanguageTypes_1 = require("../jsonLanguageTypes");
    const l10n = require("@vscode/l10n");
    const objects_1 = require("../utils/objects");
    class JSONValidation {
        constructor(jsonSchemaService, promiseConstructor) {
            this.jsonSchemaService = jsonSchemaService;
            this.promise = promiseConstructor;
            this.validationEnabled = true;
        }
        configure(raw) {
            if (raw) {
                this.validationEnabled = raw.validate !== false;
                this.commentSeverity = raw.allowComments ? undefined : jsonLanguageTypes_1.DiagnosticSeverity.Error;
            }
        }
        doValidation(textDocument, jsonDocument, documentSettings, schema) {
            if (!this.validationEnabled) {
                return this.promise.resolve([]);
            }
            const diagnostics = [];
            const added = {};
            const addProblem = (problem) => {
                // remove duplicated messages
                const signature = problem.range.start.line + ' ' + problem.range.start.character + ' ' + problem.message;
                if (!added[signature]) {
                    added[signature] = true;
                    diagnostics.push(problem);
                }
            };
            const getDiagnostics = (schema) => {
                let trailingCommaSeverity = documentSettings?.trailingCommas ? toDiagnosticSeverity(documentSettings.trailingCommas) : jsonLanguageTypes_1.DiagnosticSeverity.Error;
                let commentSeverity = documentSettings?.comments ? toDiagnosticSeverity(documentSettings.comments) : this.commentSeverity;
                let schemaValidation = documentSettings?.schemaValidation ? toDiagnosticSeverity(documentSettings.schemaValidation) : jsonLanguageTypes_1.DiagnosticSeverity.Warning;
                let schemaRequest = documentSettings?.schemaRequest ? toDiagnosticSeverity(documentSettings.schemaRequest) : jsonLanguageTypes_1.DiagnosticSeverity.Warning;
                if (schema) {
                    const addSchemaProblem = (errorMessage, errorCode, relatedInformation) => {
                        if (jsonDocument.root && schemaRequest) {
                            const astRoot = jsonDocument.root;
                            const property = astRoot.type === 'object' ? astRoot.properties[0] : undefined;
                            if (property && property.keyNode.value === '$schema') {
                                const node = property.valueNode || property;
                                const range = jsonLanguageTypes_1.Range.create(textDocument.positionAt(node.offset), textDocument.positionAt(node.offset + node.length));
                                addProblem(jsonLanguageTypes_1.Diagnostic.create(range, errorMessage, schemaRequest, errorCode, 'json', relatedInformation));
                            }
                            else {
                                const range = jsonLanguageTypes_1.Range.create(textDocument.positionAt(astRoot.offset), textDocument.positionAt(astRoot.offset + 1));
                                addProblem(jsonLanguageTypes_1.Diagnostic.create(range, errorMessage, schemaRequest, errorCode, 'json', relatedInformation));
                            }
                        }
                    };
                    if (schema.errors.length) {
                        const error = schema.errors[0];
                        addSchemaProblem(error.message, error.code, error.relatedInformation);
                    }
                    else if (schemaValidation) {
                        for (const warning of schema.warnings) {
                            addSchemaProblem(warning.message, warning.code, warning.relatedInformation);
                        }
                        const semanticErrors = jsonDocument.validate(textDocument, schema.schema, schemaValidation, documentSettings?.schemaDraft);
                        if (semanticErrors) {
                            semanticErrors.forEach(addProblem);
                        }
                    }
                    if (schemaAllowsComments(schema.schema)) {
                        commentSeverity = undefined;
                    }
                    if (schemaAllowsTrailingCommas(schema.schema)) {
                        trailingCommaSeverity = undefined;
                    }
                }
                for (const p of jsonDocument.syntaxErrors) {
                    if (p.code === jsonLanguageTypes_1.ErrorCode.TrailingComma) {
                        if (typeof trailingCommaSeverity !== 'number') {
                            continue;
                        }
                        p.severity = trailingCommaSeverity;
                    }
                    addProblem(p);
                }
                if (typeof commentSeverity === 'number') {
                    const message = l10n.t('Comments are not permitted in JSON.');
                    jsonDocument.comments.forEach(c => {
                        addProblem(jsonLanguageTypes_1.Diagnostic.create(c, message, commentSeverity, jsonLanguageTypes_1.ErrorCode.CommentNotPermitted));
                    });
                }
                return diagnostics;
            };
            if (schema) {
                const uri = schema.id || ('schemaservice://untitled/' + idCounter++);
                const handle = this.jsonSchemaService.registerExternalSchema({ uri, schema });
                return handle.getResolvedSchema().then(resolvedSchema => {
                    return getDiagnostics(resolvedSchema);
                });
            }
            return this.jsonSchemaService.getSchemaForResource(textDocument.uri, jsonDocument).then(schema => {
                return getDiagnostics(schema);
            });
        }
        getLanguageStatus(textDocument, jsonDocument) {
            return { schemas: this.jsonSchemaService.getSchemaURIsForResource(textDocument.uri, jsonDocument) };
        }
    }
    exports.JSONValidation = JSONValidation;
    let idCounter = 0;
    function schemaAllowsComments(schemaRef) {
        if (schemaRef && typeof schemaRef === 'object') {
            if ((0, objects_1.isBoolean)(schemaRef.allowComments)) {
                return schemaRef.allowComments;
            }
            if (schemaRef.allOf) {
                for (const schema of schemaRef.allOf) {
                    const allow = schemaAllowsComments(schema);
                    if ((0, objects_1.isBoolean)(allow)) {
                        return allow;
                    }
                }
            }
        }
        return undefined;
    }
    function schemaAllowsTrailingCommas(schemaRef) {
        if (schemaRef && typeof schemaRef === 'object') {
            if ((0, objects_1.isBoolean)(schemaRef.allowTrailingCommas)) {
                return schemaRef.allowTrailingCommas;
            }
            const deprSchemaRef = schemaRef;
            if ((0, objects_1.isBoolean)(deprSchemaRef['allowsTrailingCommas'])) { // deprecated
                return deprSchemaRef['allowsTrailingCommas'];
            }
            if (schemaRef.allOf) {
                for (const schema of schemaRef.allOf) {
                    const allow = schemaAllowsTrailingCommas(schema);
                    if ((0, objects_1.isBoolean)(allow)) {
                        return allow;
                    }
                }
            }
        }
        return undefined;
    }
    function toDiagnosticSeverity(severityLevel) {
        switch (severityLevel) {
            case 'error': return jsonLanguageTypes_1.DiagnosticSeverity.Error;
            case 'warning': return jsonLanguageTypes_1.DiagnosticSeverity.Warning;
            case 'ignore': return undefined;
        }
        return undefined;
    }
});
