"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModelInterface = exports.newAddInfoAttribute = exports.addInfoAttribute = void 0;
const codemodel_v3_1 = require("@azure-tools/codemodel-v3");
const codegen_csharp_1 = require("@azure-tools/codegen-csharp");
const clientruntime_1 = require("../clientruntime");
const codemodel_1 = require("@autorest/codemodel");
const linq_1 = require("@azure-tools/linq");
const class_1 = require("../../cmdlets/class");
const exports_1 = require("../exports");
function addInfoAttribute(targetProperty, pType, isRequired, isReadOnly, description, serializedName) {
    let pt = pType;
    while (pt.elementType) {
        switch (pt.elementType.schema.type) {
            case codemodel_v3_1.JsonType.Object:
                if (pt.elementType.schema.details.csharp.interfaceImplementation) {
                    pt = {
                        declaration: pt.elementType.schema.details.csharp.interfaceImplementation.declaration,
                        schema: pt.elementType.schema,
                    };
                }
                else {
                    // arg! it's not done yet. Hope it's not polymorphic itself. 
                    pt = {
                        declaration: `${pt.elementType.schema.details.csharp.namespace}.${pt.elementType.schema.details.csharp.interfaceName}`,
                        schema: pt.elementType.schema,
                    };
                }
                break;
            case codemodel_v3_1.JsonType.Array:
                pt = pt.elementType;
                break;
            default:
                pt = pt.elementType;
                break;
        }
    }
    const ptypes = new Array();
    if (pt.schema && pt.schema.details.csharp.byReference) {
        ptypes.push(`typeof(${pt.schema.details.csharp.namespace}.${pt.schema.details.csharp.interfaceName}_Reference)`);
        // do we need polymorphic types for by-resource ? Don't think so.
    }
    else {
        ptypes.push(`typeof(${pt.declaration})`);
        if (pt.schema && pt.schema.details.csharp.classImplementation && pt.schema.details.csharp.classImplementation.discriminators) {
            ptypes.push(...[...pt.schema.details.csharp.classImplementation.discriminators.values()].map(each => `typeof(${each.modelInterface.fullName})`));
        }
    }
    targetProperty.add(new codegen_csharp_1.Attribute(clientruntime_1.ClientRuntime.InfoAttribute, {
        parameters: [
            new codegen_csharp_1.LiteralExpression(`\nRequired = ${isRequired}`),
            new codegen_csharp_1.LiteralExpression(`\nReadOnly = ${isReadOnly}`),
            new codegen_csharp_1.LiteralExpression(`\nDescription = ${new codegen_csharp_1.StringExpression(description).value}`),
            new codegen_csharp_1.LiteralExpression(`\nSerializedName = ${new codegen_csharp_1.StringExpression(serializedName).value}`),
            new codegen_csharp_1.LiteralExpression(`\nPossibleTypes = new [] { ${ptypes.join(',').replace(/\?/g, '').replace(/undefined\./g, '')} }`),
        ]
    }));
}
exports.addInfoAttribute = addInfoAttribute;
function newAddInfoAttribute(targetProperty, pType, isRequired, isReadOnly, mutability, description, serializedName) {
    let pt = pType;
    while (pt.elementType) {
        switch (pt.elementType.schema.type) {
            case codemodel_v3_1.JsonType.Object:
                if (pt.elementType.schema.language.csharp.interfaceImplementation) {
                    pt = {
                        declaration: pt.elementType.schema.language.csharp.interfaceImplementation.declaration,
                        schema: pt.elementType.schema,
                    };
                }
                else {
                    // arg! it's not done yet. Hope it's not polymorphic itself. 
                    pt = {
                        declaration: `${pt.elementType.schema.language.csharp.namespace}.${pt.elementType.schema.language.csharp.interfaceName}`,
                        schema: pt.elementType.schema,
                    };
                }
                break;
            case codemodel_v3_1.JsonType.Array:
                pt = pt.elementType;
                break;
            default:
                pt = pt.elementType;
                break;
        }
    }
    const ptypes = new Array();
    if (pt.schema && pt.schema.language.csharp.byReference) {
        ptypes.push(`typeof(${pt.schema.language.csharp.namespace}.${pt.schema.language.csharp.interfaceName}_Reference)`);
        // do we need polymorphic types for by-resource ? Don't think so.
    }
    else {
        ptypes.push(`typeof(${pt.declaration})`);
        if (pt.schema && pt.schema.language.csharp.classImplementation && pt.schema.language.csharp.classImplementation.discriminators) {
            ptypes.push(...[...pt.schema.language.csharp.classImplementation.discriminators.values()].map(each => `typeof(${each.modelInterface.fullName})`));
        }
    }
    targetProperty.add(new codegen_csharp_1.Attribute(clientruntime_1.ClientRuntime.InfoAttribute, {
        parameters: [
            new codegen_csharp_1.LiteralExpression(`\nRequired = ${isRequired}`),
            new codegen_csharp_1.LiteralExpression(`\nReadOnly = ${isReadOnly}`),
            new codegen_csharp_1.LiteralExpression(`\nRead = ${mutability.read}`),
            new codegen_csharp_1.LiteralExpression(`\nCreate = ${mutability.create}`),
            new codegen_csharp_1.LiteralExpression(`\nUpdate = ${mutability.update}`),
            new codegen_csharp_1.LiteralExpression(`\nDescription = ${new codegen_csharp_1.StringExpression(description !== null && description !== void 0 ? description : '').value}`),
            new codegen_csharp_1.LiteralExpression(`\nSerializedName = ${new codegen_csharp_1.StringExpression(serializedName).value}`),
            new codegen_csharp_1.LiteralExpression(`\nPossibleTypes = new [] { ${ptypes.join(',').replace(/\?/g, '').replace(/undefined\./g, '')} }`),
        ]
    }));
}
exports.newAddInfoAttribute = newAddInfoAttribute;
class ModelInterface extends codegen_csharp_1.Interface {
    get schema() {
        return this.classImplementation.schema;
    }
    get defaultOfType() {
        return this.classImplementation.defaultOfType;
    }
    get convertObjectMethod() {
        return this.classImplementation.convertObjectMethod;
    }
    deserializeFromContainerMember(mediaType, container, serializedName, defaultValue) {
        return this.classImplementation.deserializeFromContainerMember(mediaType, container, serializedName, defaultValue);
    }
    deserializeFromNode(mediaType, node, defaultValue) {
        return this.classImplementation.deserializeFromNode(mediaType, node, defaultValue);
    }
    /** emits an expression to deserialize content from a string */
    deserializeFromString(mediaType, content, defaultValue) {
        return this.classImplementation.deserializeFromString(mediaType, content, defaultValue);
    }
    /** emits an expression to deserialize content from a content/response */
    deserializeFromResponse(mediaType, content, defaultValue) {
        return this.classImplementation.deserializeFromResponse(mediaType, content, defaultValue);
    }
    /** emits an expression serialize this to a HttpContent */
    serializeToContent(mediaType, value, mode) {
        return this.classImplementation.serializeToContent(mediaType, value, mode);
    }
    serializeToNode(mediaType, value, serializedName, mode) {
        return this.classImplementation.serializeToNode(mediaType, value, serializedName, mode);
    }
    serializeToContainerMember(mediaType, value, container, serializedName, mode) {
        return this.classImplementation.serializeToContainerMember(mediaType, value, container, serializedName, mode);
    }
    get isXmlAttribute() {
        return this.classImplementation.isXmlAttribute;
    }
    get isRequired() {
        return this.classImplementation.isRequired;
    }
    validatePresence(eventListener, property) {
        return this.classImplementation.validatePresence(eventListener, property);
    }
    validateValue(eventListener, property) {
        return this.classImplementation.validateValue(eventListener, property);
    }
    get hasHeaderProperties() {
        // disabled
        // return this.classImplementation.hasHeaderProperties; 
        return false;
    }
    constructor(parent, interfaceName, classImplementation, state, objectInitializer) {
        super(parent, interfaceName);
        this.classImplementation = classImplementation;
        this.state = state;
        this.isNullable = true;
        this.partial = true;
        this.apply(objectInitializer);
    }
    get isInternal() {
        return this.accessModifier === codegen_csharp_1.Access.Internal;
    }
    init() {
        this.init = () => { }; // only allow a single init!
        this.schema.language.csharp = this.schema.language.csharp || new codemodel_1.Language();
        const implData = (this.schema.language.csharp = this.schema.language.csharp || {});
        //implData.interfaceImplementation = this;
        this.description = `${this.schema.language.csharp.description}`;
        const virtualProperties = this.schema.language.csharp.virtualProperties || {
            owned: [],
            inherited: [],
            inlined: []
        };
        if (this.schema.language.csharp.virtualProperties) {
            for (const virtualProperty of (0, linq_1.values)(virtualProperties.owned)) {
                if (virtualProperty.private && !this.isInternal) {
                    continue;
                }
                const modelProperty = virtualProperty.property;
                const internalSet = !!(!this.isInternal && (modelProperty.readOnly || !!virtualProperty.readOnly || modelProperty.language.csharp.constantValue));
                const isRequired = !!modelProperty.required;
                const mutability = { read: !!virtualProperty.read, update: !!virtualProperty.update, create: !!virtualProperty.create };
                const pType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(modelProperty.schema, isRequired, this.state.path('schema'));
                const p = this.add(new codegen_csharp_1.InterfaceProperty(virtualProperty.name, pType, {
                    description: modelProperty.language.default.description,
                    setAccess: internalSet ? codegen_csharp_1.Access.Internal : codegen_csharp_1.Access.Public
                }));
                this.addInfoAttribute(p, pType, isRequired, internalSet, mutability, modelProperty.language.default.description, modelProperty.serializedName);
                if ((0, class_1.isEnumImplementation)(modelProperty.schema)) {
                    (0, class_1.addPSArgumentCompleterAttribute)(p, modelProperty.schema);
                }
                else if (pType instanceof exports_1.ArrayOf && (0, class_1.isEnumImplementation)(modelProperty.schema.elementType)) {
                    (0, class_1.addPSArgumentCompleterAttribute)(p, modelProperty.schema.elementType);
                }
                if (!this.isInternal && modelProperty.language.csharp.constantValue !== undefined) {
                    p.setAccess = codegen_csharp_1.Access.Internal;
                }
            }
            for (const virtualProperty of (0, linq_1.values)(virtualProperties.inlined)) {
                // don't publicly expose the 'private' properties.
                if (virtualProperty.private && !this.isInternal) {
                    continue;
                }
                const modelProperty = virtualProperty.property;
                const isRequired = !!virtualProperty.required;
                const mutability = { read: !!virtualProperty.read, update: !!virtualProperty.update, create: !!virtualProperty.create };
                const pType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(modelProperty.schema, isRequired, this.state.path('schema'));
                const internalSet = !!(!this.isInternal && (modelProperty.readOnly || !!virtualProperty.readOnly || modelProperty.language.csharp.constantValue));
                const p = this.add(new codegen_csharp_1.InterfaceProperty(virtualProperty.name, pType, {
                    description: modelProperty.language.default.description,
                    setAccess: internalSet ? codegen_csharp_1.Access.Internal : codegen_csharp_1.Access.Public
                }));
                this.addInfoAttribute(p, pType, isRequired, internalSet, mutability, modelProperty.language.default.description, modelProperty.serializedName);
                if ((0, class_1.isEnumImplementation)(modelProperty.schema)) {
                    (0, class_1.addPSArgumentCompleterAttribute)(p, modelProperty.schema);
                }
                else if (pType instanceof exports_1.ArrayOf && (0, class_1.isEnumImplementation)(modelProperty.schema.elementType)) {
                    (0, class_1.addPSArgumentCompleterAttribute)(p, modelProperty.schema.elementType);
                }
            }
        }
        if (!this.isInternal) {
            // mark it as json serializable
            if (!this.schema.language.csharp.isHeaderModel) {
                if (this.state.project.jsonSerialization) {
                    this.interfaces.push(clientruntime_1.ClientRuntime.IJsonSerializable);
                }
                if (this.state.project.xmlSerialization) {
                    this.interfaces.push(clientruntime_1.ClientRuntime.IXmlSerializable);
                }
            }
        }
        return this;
    }
    addInfoAttribute(p, pType, isRequired, internalSet, mutability, description, serializedName) {
        if (!this.isInternal) {
            return newAddInfoAttribute(p, pType, isRequired, internalSet, mutability, description, serializedName);
        }
    }
}
exports.ModelInterface = ModelInterface;
//# sourceMappingURL=interface.js.map