"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Helper = void 0;
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
const schema_1 = require("../utils/schema");
const codemodel_1 = require("@autorest/codemodel");
const linq_1 = require("@azure-tools/linq");
const codegen_1 = require("@azure-tools/codegen");
class Helper {
    constructor(useDateTimeOffset = false) {
        this.useDateTimeOffset = useDateTimeOffset;
    }
    HasConstrains(schema) {
        if (schema.minimum !== undefined || schema.maximum !== undefined || schema.maxLength !== undefined || schema.minLength !== undefined || schema.maxItems !== undefined || schema.minItems !== undefined || schema.multipleOf !== undefined || schema.pattern !== undefined || schema.uniqueItems !== undefined) {
            return true;
        }
        return false;
    }
    HasConstantProperty(schema) {
        const virtualProperties = this.GetAllPublicVirtualProperties(schema.language.default.virtualProperties);
        return virtualProperties.filter(p => p.required && (p.property.schema.type === codemodel_1.SchemaType.Constant || this.IsConstantEnumProperty(p) || (p.property.schema.type === codemodel_1.SchemaType.Object && this.HasConstantProperty(p.property.schema)))).length > 0;
    }
    GetCsharpType(schema) {
        let type = schema.type;
        if (schema.type === codemodel_1.SchemaType.Integer || schema.type === codemodel_1.SchemaType.Number) {
            type = type + schema.precision;
        }
        const offset = this.useDateTimeOffset ? 'Offset' : '';
        const typeMap = new Map([
            ['integer', 'int'],
            ['integer32', 'int'],
            ['integer64', 'long'],
            ['number32', 'double'],
            ['number64', 'double'],
            ['number128', 'decimal'],
            ['boolean', 'bool'],
            ['string', 'string'],
            ['unixtime', 'System.DateTime'],
            ['credential', 'string'],
            ['byte-array', 'byte[]'],
            ['duration', 'System.TimeSpan'],
            ['uuid', 'System.Guid'],
            ['date-time', 'System.DateTime' + offset],
            ['date', 'System.DateTime'],
            ['binary', 'string'],
            ['uri', 'string'],
            ['arm-id', 'string']
        ]);
        if (typeMap.has(type)) {
            return typeMap.get(type);
        }
        return '';
    }
    isArraySchema(schema) {
        return schema.type === codemodel_1.SchemaType.Array;
    }
    isDictionarySchema(schema) {
        return schema.type === codemodel_1.SchemaType.Dictionary;
    }
    ShouldValidate(schema) {
        if (!schema) {
            return false;
        }
        const typesToValidate = new Array();
        const validatedTypes = new Set();
        typesToValidate.push(schema);
        while (typesToValidate.length > 0) {
            const modelToValidate = typesToValidate.pop();
            if (!modelToValidate) {
                continue;
            }
            else {
                if (validatedTypes.has(modelToValidate)) {
                    continue;
                }
                validatedTypes.add(modelToValidate);
                if (this.isArraySchema(modelToValidate) || this.isDictionarySchema(modelToValidate)) {
                    typesToValidate.push(modelToValidate.elementType);
                }
                else if ((0, codemodel_1.isObjectSchema)(modelToValidate)) {
                    const virtualProperties = modelToValidate.extensions && modelToValidate.extensions['x-ms-azure-resource'] ? (0, schema_1.getAllPublicVirtualPropertiesForSdkWithoutInherited)(modelToValidate.language.default.virtualProperties) : (0, schema_1.getAllPublicVirtualPropertiesForSdk)(modelToValidate.language.default.virtualProperties);
                    (0, linq_1.values)(virtualProperties).where(p => (0, codemodel_1.isObjectSchema)(p.property.schema)).forEach(cp => typesToValidate.push(cp.property.schema));
                    if ((0, linq_1.values)(virtualProperties).any(p => (p.required && p.property.schema.type !== codemodel_1.SchemaType.Constant && !this.IsConstantEnumProperty(p)) || this.HasConstrains(p.property.schema))) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    appendConstraintValidations(valueReference, sb, model) {
        const schema = model;
        if (schema.maximum !== undefined) {
            const rule = schema.exclusiveMaximum ? 'ExclusiveMaximum' : 'InclusiveMaximum';
            const cmp = schema.exclusiveMaximum ? '>=' : '>';
            sb.push(`if (${valueReference} ${cmp} ${schema.maximum})`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.${rule}, "${valueReference.replace('this.', '')}", ${schema.maximum});`);
            sb.push('}');
        }
        if (schema.minimum !== undefined) {
            const rule = schema.exclusiveMinimum ? 'ExclusiveMinimum' : 'InclusiveMinimum';
            const cmp = schema.exclusiveMinimum ? '<=' : '<';
            sb.push(`if (${valueReference} ${cmp} ${schema.minimum})`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.${rule}, "${valueReference.replace('this.', '')}", ${schema.minimum});`);
            sb.push('}');
        }
        if (schema.maxItems !== undefined) {
            sb.push(`if (${valueReference}.Count > ${schema.maxItems})`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.MaxItems, "${valueReference.replace('this.', '')}", ${schema.maxItems});`);
            sb.push('}');
        }
        if (schema.maxLength !== undefined) {
            sb.push(`if (${valueReference}.Length > ${schema.maxLength})`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.MaxLength, "${valueReference.replace('this.', '')}", ${schema.maxLength});`);
            sb.push('}');
        }
        if (schema.minLength !== undefined) {
            sb.push(`if (${valueReference}.Length < ${schema.minLength})`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.MinLength, "${valueReference.replace('this.', '')}", ${schema.minLength});`);
            sb.push('}');
        }
        if (schema.minItems !== undefined) {
            sb.push(`if (${valueReference}.Count < ${schema.minItems})`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.MinItems, "${valueReference.replace('this.', '')}", ${schema.minItems});`);
            sb.push('}');
        }
        if (schema.multipleOf !== undefined) {
            sb.push(`if (${valueReference} % ${schema.multipleOf} != 0)`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.MultipleOf, "${valueReference.replace('this.', '')}", ${schema.multipleOf});`);
            sb.push('}');
        }
        if (schema.pattern !== undefined) {
            // eslint-disable-next-line
            const constraintValue = "\"" + schema.pattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
            let condition = `!System.Text.RegularExpressions.Regex.IsMatch(${valueReference}, ${constraintValue})`;
            if (schema.type === codemodel_1.SchemaType.Dictionary) {
                condition = `!System.Linq.Enumerable.All(${valueReference}.Values, value => System.Text.RegularExpressions.Regex.IsMatch(value, ${constraintValue}))`;
            }
            sb.push(`if (${condition})`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.Pattern, "${valueReference.replace('this.', '')}", ${constraintValue});`);
            sb.push('}');
        }
        if (schema.uniqueItems !== undefined && 'true' === schema.uniqueItems.toString()) {
            sb.push(`if (${valueReference}.Count != System.Linq.Enumerable.Count(System.Linq.Enumerable.Distinct(${valueReference})))`);
            sb.push('{');
            sb.push(`    throw new Microsoft.Rest.ValidationException(Microsoft.Rest.ValidationRules.UniqueItems, "${valueReference.replace('this.', '')}");`);
            sb.push('}');
        }
    }
    isKindOfString(schema, required = false) {
        var _a;
        if (schema.type === codemodel_1.SchemaType.String) {
            return true;
        }
        else if (schema.type === codemodel_1.SchemaType.Constant && schema.valueType.type === codemodel_1.SchemaType.String) {
            return true;
        }
        else if (schema.type === codemodel_1.SchemaType.Choice && schema.choiceType.type === codemodel_1.SchemaType.String) {
            return true;
        }
        else if (schema.type === codemodel_1.SchemaType.SealedChoice && schema.choiceType.type === codemodel_1.SchemaType.String && (((_a = schema.extensions) === null || _a === void 0 ? void 0 : _a['x-ms-model-as-string']) === true || (required && schema.choices.length === 1))) {
            return true;
        }
        // ToDo: we need to figure how to handle the case when schema type is enum
        // Skip it since there is a bug in IsKindOfString in the csharp generator
        // if (schema.type === SchemaType.Choice && (<ChoiceSchema>schema).choiceType.type === SchemaType.String) {
        //   return true;
        // }
        // if (schema.type === SchemaType.SealedChoice
        //   && (<ChoiceSchema>schema).choiceType.type === SchemaType.String) {
        //   // currently assume modelAsString true
        //   return true;
        // }
        return false;
    }
    PathParameterString(parameter, clientPrefix) {
        var _a;
        if (!['path', 'query'].includes((_a = parameter.protocol.http) === null || _a === void 0 ? void 0 : _a.in)) {
            return '';
        }
        const prefix = parameter.implementation === 'Client' ? `this${clientPrefix}.` : '';
        let res = '';
        if (this.isKindOfString(parameter.schema, parameter.required)) {
            res = `${prefix}${parameter.language.default.name}`;
        }
        else {
            let serializationSettings = `this${clientPrefix}.SerializationSettings`;
            if (this.IsValueType(parameter.schema.type)) {
                if (parameter.schema.type === codemodel_1.SchemaType.Date) {
                    serializationSettings = 'new Microsoft.Rest.Serialization.DateJsonConverter()';
                }
                else if (parameter.schema.type === codemodel_1.SchemaType.DateTime && parameter.schema.format === 'date-time-rfc1123') {
                    serializationSettings = 'new Microsoft.Rest.Serialization.DateTimeRfc1123JsonConverter()';
                }
                else if (parameter.schema.type === codemodel_1.SchemaType.Uri) {
                    serializationSettings = 'new Microsoft.Rest.Serialization.Base64UrlJsonConverter()';
                }
                else if (parameter.schema.type === codemodel_1.SchemaType.UnixTime) {
                    serializationSettings = 'new Microsoft.Rest.Serialization.UnixTimeJsonConverter()';
                }
            }
            res = `Microsoft.Rest.Serialization.SafeJsonConvert.SerializeObject(${prefix}${parameter.language.default.name}, ${serializationSettings}).Trim('"')`;
        }
        if (parameter.extensions && parameter.extensions['x-ms-skip-url-encoding']) {
            return res;
        }
        else {
            return `System.Uri.EscapeDataString(${res})`;
        }
    }
    ValidateType(schema, scope, valueReference, isNullable, indentation = 3) {
        const indentationSpaces = '    '.repeat(indentation);
        const sb = new Array();
        if (!scope) {
            throw new Error('scope is null');
        }
        if (!!schema && (0, codemodel_1.isObjectSchema)(schema) && this.ShouldValidateChain(schema)) {
            sb.push(`${valueReference}.Validate();`);
        }
        if (this.HasConstrains(schema)) {
            this.appendConstraintValidations(valueReference, sb, schema);
        }
        if (schema && this.isArraySchema(schema) && this.ShouldValidateChain(schema)) {
            // ToDo: Should try to get a unique name instead of element
            let elementVar = 'element';
            if (valueReference.startsWith(elementVar)) {
                elementVar = valueReference + '1';
            }
            const innerValidation = this.ValidateType(schema.elementType, scope, elementVar, true, 1);
            if (innerValidation) {
                sb.push(`foreach (var ${elementVar} in ${valueReference})`);
                sb.push('{');
                innerValidation.split('\r\n').map(str => sb.push(str));
                sb.push('}');
            }
        }
        else if (schema && this.isDictionarySchema(schema) && this.ShouldValidateChain(schema)) {
            // ToDo: Should try to get a unique name instead of valueElement
            let valueVar = 'valueElement';
            if (valueReference.startsWith(valueVar)) {
                valueVar = valueReference + '1';
            }
            const innerValidation = this.ValidateType(schema.elementType, scope, valueVar, true, 1);
            if (innerValidation) {
                sb.push(`foreach (var ${valueVar} in ${valueReference}.Values)`);
                sb.push('{');
                innerValidation.split('\r\n').map(str => sb.push(str));
                sb.push('}');
            }
        }
        if (sb.length > 0) {
            if (this.IsValueType(schema.type) && !isNullable) {
                return sb.map(str => indentationSpaces + str).join('\r\n');
            }
            else {
                return `${indentationSpaces}if (${valueReference} != null)\r\n${indentationSpaces}{\r\n${sb.map(str => indentationSpaces + '    ' + str).join('\r\n')}\r\n${indentationSpaces}}`;
            }
        }
        return '';
    }
    ShouldValidateChain(schema) {
        if (!schema) {
            return false;
        }
        const typesToValidate = new Array();
        const validatedTypes = new Set();
        typesToValidate.push(schema);
        while (typesToValidate.length > 0) {
            const modelToValidate = typesToValidate.pop();
            if (!modelToValidate) {
                continue;
            }
            else {
                validatedTypes.add(modelToValidate);
                if (this.isArraySchema(modelToValidate) || this.isDictionarySchema(modelToValidate)) {
                    typesToValidate.push(modelToValidate.elementType);
                }
                else if ((0, codemodel_1.isObjectSchema)(modelToValidate)) {
                    if (this.ShouldValidate(modelToValidate)) {
                        return true;
                    }
                    if (modelToValidate.parents && (modelToValidate.parents.immediate.length === 1 && !(modelToValidate.extensions && modelToValidate.extensions['x-ms-azure-resource']))) {
                        typesToValidate.push(modelToValidate.parents.immediate[0]);
                    }
                }
            }
        }
        return false;
    }
    GetDeserializationSettings(schema, ref) {
        if (schema.type === codemodel_1.SchemaType.Date) {
            return 'new Microsoft.Rest.Serialization.DateJsonConverter()';
        }
        else if (schema.type === codemodel_1.SchemaType.Uri) {
            return 'new Microsoft.Rest.Serialization.Base64UrlJsonConverter()';
        }
        else if (schema.type === codemodel_1.SchemaType.UnixTime) {
            return 'new Microsoft.Rest.Serialization.UnixTimeJsonConverter()';
        }
        return ref + '.DeserializationSettings';
    }
    GetSerializationSettings(schema, ref) {
        if (schema.type === codemodel_1.SchemaType.Date) {
            return 'new Microsoft.Rest.Serialization.DateJsonConverter()';
        }
        else if (schema.type === codemodel_1.SchemaType.Uri) {
            return 'new Microsoft.Rest.Serialization.Base64UrlJsonConverter()';
        }
        else if (schema.type === codemodel_1.SchemaType.UnixTime) {
            return 'new Microsoft.Rest.Serialization.UnixTimeJsonConverter()';
        }
        return ref + '.SerializationSettings';
    }
    IsNullCheckRequiredForVirtualProperty(virtualProperty) {
        return !((this.IsValueType(virtualProperty.property.schema.type) || (virtualProperty.property.schema.type === codemodel_1.SchemaType.SealedChoice && (virtualProperty.property.schema.choiceType.type !== codemodel_1.SchemaType.String || (virtualProperty.property.schema.extensions && !virtualProperty.property.schema.extensions['x-ms-model-as-string'])))) && (virtualProperty.required || virtualProperty.property.nullable === false));
    }
    CamelCase(str) {
        // str = str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
        //   return index === 0 ? word.toLowerCase() : word.toUpperCase();
        // }).replace(/\s+/g, '');
        return (0, codegen_1.camelCase)(str);
    }
    PascalCase(str) {
        return (0, codegen_1.pascalCase)(str);
    }
    GetAllPublicVirtualProperties(virtualProperties) {
        return (0, schema_1.getAllPublicVirtualPropertiesForSdk)(virtualProperties);
    }
    GetAllPublicVirtualPropertiesWithoutInherited(virtualProperties) {
        return (0, schema_1.getAllPublicVirtualPropertiesForSdkWithoutInherited)(virtualProperties);
    }
    NeedsTransformationConverter(object) {
        for (const property of (0, linq_1.values)(object.properties)) {
            if (property.extensions && property.extensions['x-ms-client-flatten']) {
                return true;
            }
        }
        return false;
    }
    IsValueType(type) {
        if (['boolean', 'integer', 'number', 'unixtime', 'duration', 'uuid', 'date-time', 'date'].includes(type)) {
            return true;
        }
        return false;
    }
    HandleConstParameters(operation) {
        const result = new Array();
        const bodyParameters = operation.requests && operation.requests.length > 0 ? (operation.requests[0].parameters || []).filter(each => each.protocol.http && each.protocol.http.in === 'body' && !(each.extensions && each.extensions['x-ms-client-flatten']) && each.implementation !== 'Client') : [];
        const nonBodyParameters = operation.parameters ? operation.parameters.filter(each => each.implementation !== 'Client') : [];
        const parameters = [...nonBodyParameters, ...bodyParameters].filter(each => this.IsConstantEnumParameter(each));
        for (const parameter of (0, linq_1.values)(parameters)) {
            const quote = parameter.schema.choiceType.type === codemodel_1.SchemaType.String ? '"' : '';
            const csharpType = this.GetCsharpType((parameter.schema).choiceType);
            result.push(`            ${csharpType} ${parameter.language.default.name} = ${quote}${parameter.schema.choices[0].value}${quote};`);
        }
        return result.join('\r\n');
    }
    IsConstantEnumParameter(parameter) {
        if (!parameter.required) {
            // const parameters are always required
            return false;
        }
        // skip parameter.schema.type === SchemaType.Constant, since there is a bug
        if ((parameter.schema.type === codemodel_1.SchemaType.SealedChoice && (parameter.schema).choices.length === 1)
            || (parameter.schema.type === codemodel_1.SchemaType.Choice && (parameter.schema).choices.length === 1)) {
            return true;
        }
        return false;
    }
    IsConstantParameter(parameter) {
        if (this.IsConstantEnumParameter(parameter)) {
            return true;
        }
        if (parameter.schema.type === codemodel_1.SchemaType.Constant) {
            return true;
        }
        return false;
    }
    IsConstantEnumProperty(property) {
        if (!property.required) {
            // const parameters are always required
            return false;
        }
        // skip parameter.schema.type === SchemaType.Constant, since there is a bug
        if ((property.property.schema.type === codemodel_1.SchemaType.SealedChoice && (property.property.schema).choices.length === 1)
            || (property.property.schema.type === codemodel_1.SchemaType.Choice && (property.property.schema).choices.length === 1)) {
            return true;
        }
        return false;
    }
    IsConstantProperty(property) {
        if (this.IsConstantEnumProperty(property)) {
            return true;
        }
        if (property.property.schema.type === codemodel_1.SchemaType.Constant) {
            return true;
        }
        return false;
    }
    GetUniqueName(name, usedNames) {
        let uniqueName = name;
        let i = 0;
        while (usedNames.includes(uniqueName)) {
            uniqueName = `${name}${++i}`;
        }
        return uniqueName;
    }
    GetValidCsharpName(name) {
        let validChars = name.replace(/[^a-zA-Z0-9_]/g, '');
        // prepend '_' if the name starts with a digit
        if (!/^[a-zA-Z_]/.test(validChars)) {
            validChars = '_' + validChars;
        }
        return validChars;
    }
    IsEnum(schema) {
        if (schema.type === codemodel_1.SchemaType.SealedChoice && schema.choiceType.type === codemodel_1.SchemaType.String && schema.extensions && !schema.extensions['x-ms-model-as-string']) {
            return true;
        }
        return false;
    }
    ConvertToValidMethodGroupKey(key) {
        return key.replace(/Operations$/, '');
    }
    isCloudErrorName(name) {
        // This is to work around the fact that some CloudError will be generated as CloudErrorAutoGenerated in m4
        const reg = new RegExp('^CloudErrorAutoGenerated\\d{0,1}$');
        if (name === 'CloudError' || reg.test(name)) {
            return true;
        }
        return false;
    }
    IsCloudErrorException(operation) {
        return (!operation.exceptions || !operation.exceptions[0].schema || this.isCloudErrorName(operation.exceptions[0].schema.language.default.name));
    }
    PopulateGroupParameters(parameter) {
        var _a;
        const groupParameter = parameter.language.default.name;
        const result = Array();
        for (const virtualProperty of (0, linq_1.values)((parameter.schema.language.default.virtualProperties.owned))) {
            let type = ((_a = virtualProperty.property.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.fullname) || '';
            type = (this.IsValueType(virtualProperty.property.schema.type) || (virtualProperty.property.schema.type === codemodel_1.SchemaType.SealedChoice && virtualProperty.property.schema.choiceType.type === codemodel_1.SchemaType.String && virtualProperty.property.schema.extensions && !virtualProperty.property.schema.extensions['x-ms-model-as-string'])) && !virtualProperty.required ? `${type}?` : type;
            const CamelName = (0, codegen_1.camelCase)(virtualProperty.name);
            result.push(`            ${type} ${CamelName} = default(${type});`);
            result.push(`            if (${groupParameter} != null)`);
            result.push('            {');
            result.push(`                ${CamelName} = ${groupParameter}.${(0, codegen_1.pascalCase)(CamelName)};`);
            result.push('            }');
        }
        if (result.length > 0) {
            return result.join('\r\n');
        }
        return '';
    }
    wrapComments(indentation, prefix, comments) {
        const defaultMaximumCommentColumns = 80;
        if (comments === null || comments === undefined || comments.length === 0) {
            return '';
        }
        //cannot predict indentation because we cannot get last line generated
        const length = defaultMaximumCommentColumns - prefix.length - 1;
        const result = this.lineBreak(comments, length);
        for (let i = 0; i < result.length; i++) {
            if (i != 0) {
                result[i] = prefix + result[i];
            }
        }
        return result.join('\n' + indentation);
    }
    lineBreak(comments, length) {
        const splitter = /\r\n|\r|\n/i;
        const lines = new Array();
        for (const line of comments.split(splitter)) {
            let processedLine = line;
            while (processedLine.length > 0) {
                processedLine = processedLine.trim();
                const whiteSpacePositions = [...new Array(processedLine.length).keys()].filter(i => /\s/.test(processedLine[i])).concat([processedLine.length]);
                let preWidthWrapAt = 0;
                let postWidthWrapAt = 0;
                for (const index of whiteSpacePositions) {
                    if (index <= length) {
                        preWidthWrapAt = index;
                    }
                    else if (postWidthWrapAt === 0) {
                        postWidthWrapAt = index;
                    }
                }
                const wrapAt = preWidthWrapAt != 0 ? preWidthWrapAt : (postWidthWrapAt != 0 ? postWidthWrapAt : processedLine.length);
                lines.push(processedLine.substring(0, wrapAt));
                processedLine = processedLine.substring(wrapAt);
            }
        }
        return lines;
    }
}
exports.Helper = Helper;
//# sourceMappingURL=utility.js.map