"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModelClass = exports.getVirtualPropertyName = 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 codemodel_v3_1 = require("@azure-tools/codemodel-v3");
const codegen_1 = require("@azure-tools/codegen");
const linq_1 = require("@azure-tools/linq");
const codegen_csharp_1 = require("@azure-tools/codegen-csharp");
const clientruntime_1 = require("../clientruntime");
const interface_1 = require("./interface");
const model_class_json_1 = require("./model-class-json");
const property_1 = require("./property");
const csharp_declarations_1 = require("../csharp-declarations");
const model_class_dictionary_1 = require("./model-class-dictionary");
const codemodel_1 = require("@autorest/codemodel");
const schema_1 = require("../../utils/schema");
function getVirtualPropertyName(vp) {
    var _a;
    if (vp && vp.accessViaMember && ((_a = vp.accessViaProperty) === null || _a === void 0 ? void 0 : _a.accessViaMember)) {
        return getVirtualPropertyName(vp.accessViaMember);
    }
    return vp ? vp.name : '';
}
exports.getVirtualPropertyName = getVirtualPropertyName;
class ModelClass extends codegen_csharp_1.Class {
    deserializeFromContainerMember(mediaType, container, serializedName, defaultValue) {
        return this.featureImplementation.deserializeFromContainerMember(mediaType, container, serializedName, defaultValue);
    }
    deserializeFromNode(mediaType, node, defaultValue) {
        return this.featureImplementation.deserializeFromNode(mediaType, node, defaultValue);
    }
    serializeToNode(mediaType, value, serializedName, mode) {
        return this.featureImplementation.serializeToNode(mediaType, value, serializedName, mode);
    }
    get defaultOfType() {
        return (0, codegen_csharp_1.toExpression)('null /* model class */');
    }
    get convertObjectMethod() {
        return this.featureImplementation.convertObjectMethod;
    }
    /** emits an expression serialize this to a HttpContent */
    serializeToContent(mediaType, value, mode) {
        return this.featureImplementation.serializeToContent(mediaType, value, mode);
    }
    /** emits an expression to deserialize content from a string */
    deserializeFromString(mediaType, content, defaultValue) {
        return this.featureImplementation.deserializeFromString(mediaType, content, defaultValue);
    }
    /** emits an expression to deserialize content from a content/response */
    deserializeFromResponse(mediaType, content, defaultValue) {
        return this.featureImplementation.deserializeFromResponse(mediaType, content, defaultValue);
    }
    serializeToContainerMember(mediaType, value, container, serializedName, mode) {
        return this.featureImplementation.serializeToContainerMember(mediaType, value, container, serializedName, mode);
    }
    get isXmlAttribute() {
        return this.featureImplementation.isXmlAttribute;
    }
    get isRequired() {
        return this.featureImplementation.isRequired;
    }
    get schema() { return this.featureImplementation.schema; }
    /* @internal */ get modelInterface() { var _a; return (_a = this.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.interfaceImplementation; }
    /* @internal */ get internalModelInterface() { var _a; return (_a = this.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.internalInterfaceImplementation; }
    // public hasHeaderProperties: boolean;
    constructor(namespace, schemaWithFeatures, state, objectInitializer) {
        var _a, _b, _c;
        super(namespace, ((_a = schemaWithFeatures.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.name) || '');
        this.isNullable = true;
        this.isPolymorphic = false;
        /* @internal */ this.discriminators = new Map();
        /* @internal */ this.parentModelClasses = new Array();
        /* @internal */ this.backingFields = new Array();
        /* @internal */ this.validationEventListener = new codegen_csharp_1.Parameter('eventListener', clientruntime_1.ClientRuntime.IEventListener, { description: `an <see cref="${clientruntime_1.ClientRuntime.IEventListener}" /> instance that will receive validation events.` });
        this.validationStatements = new codegen_csharp_1.Statements();
        this.ownedProperties = new Array();
        this.pMap = new Map();
        this.featureImplementation = schemaWithFeatures;
        this.schema.language.csharp = this.schema.language.csharp || new codemodel_1.Language();
        this.schema.language.csharp.classImplementation = this; // mark the code-model with the class we're creating.
        this.state = state;
        this.apply(objectInitializer);
        if (this.schema.language.csharp.suppressFormat) {
            this.add(new codegen_csharp_1.Attribute(csharp_declarations_1.DoNotFormatAttribute));
        }
        // must be a partial class
        this.partial = true;
        this.handleDiscriminator();
        // create an interface for this model class
        if (!this.schema.language.csharp.interfaceImplementation) {
            (this.schema.language.csharp.interfaceImplementation = new interface_1.ModelInterface(this.namespace, this.schema.language.csharp.interfaceName || `I${this.schema.language.csharp.name}`, this, this.state));
        }
        this.interfaces.push(this.modelInterface);
        if (!this.schema.language.csharp.internalInterfaceImplementation) {
            (this.schema.language.csharp.internalInterfaceImplementation = new interface_1.ModelInterface(this.namespace, this.schema.language.csharp.internalInterfaceName || `I${this.schema.language.csharp.name}Internal`, this, this.state, { accessModifier: codegen_csharp_1.Access.Internal }));
        }
        this.interfaces.push(this.internalModelInterface);
        this.schema.language.csharp.internalInterfaceImplementation.init();
        this.schema.language.csharp.interfaceImplementation.init();
        // add default constructor
        this.constructorMethod = this.addMethod(new codegen_csharp_1.Constructor(this, { description: `Creates an new <see cref="${this.name}" /> instance.` })); // default constructor for fits and giggles.
        // handle parent interface implementation
        if (!this.handleAllOf()) {
            // handle the AdditionalProperties if used
            const dictSchema = this.schema.type === codemodel_1.SchemaType.Dictionary ? this.schema :
                (_c = (_b = this.schema.parents) === null || _b === void 0 ? void 0 : _b.immediate) === null || _c === void 0 ? void 0 : _c.find((schema) => schema.type === codemodel_1.SchemaType.Dictionary);
            if (dictSchema) {
                this.dictionaryImpl = new model_class_dictionary_1.DictionaryImplementation(this).init();
            }
        }
        // create the properties for this schema
        this.createProperties();
        // add validation implementation
        this.addValidation();
        // add header properties for this model.
        // DISABLED.
        this.addHeaderDeserializer();
        if (this.state.project.jsonSerialization) {
            this.jsonSerializer = new model_class_json_1.JsonSerializableClass(this);
        }
    }
    nested(virtualProperty, internal) {
        if (virtualProperty.accessViaProperty) {
            if (virtualProperty.accessViaProperty.accessViaProperty) {
                // return `/*1*/${getVirtualPropertyName(virtualProperty.accessViaMember)}.${this.nested(virtualProperty.accessViaProperty.accessViaProperty, internal)}`;
                return `${getVirtualPropertyName(virtualProperty.accessViaMember)}.${this.nested(virtualProperty.accessViaProperty.accessViaProperty, internal)}`;
            }
        }
        //return `/*2*/${getVirtualPropertyName(virtualProperty.accessViaMember)}`;
        return `${getVirtualPropertyName(virtualProperty.accessViaMember)}`;
    }
    accessor(virtualProperty, internal = false) {
        var _a;
        if (virtualProperty.accessViaProperty) {
            const prefix = virtualProperty.accessViaProperty.accessViaProperty ? this.nested(virtualProperty.accessViaProperty.accessViaProperty, internal) : '';
            const containingProperty = this.pMap.get(virtualProperty.accessViaProperty);
            if (containingProperty && virtualProperty.accessViaMember) {
                //return `/*3*/((${virtualProperty.accessViaMember.originalContainingSchema.details.csharp.fullInternalInterfaceName})${containingProperty.name}${prefix}).${getVirtualPropertyName(virtualProperty.accessViaMember)}`;
                return `((${(_a = virtualProperty.accessViaMember.originalContainingSchema.language.csharp) === null || _a === void 0 ? void 0 : _a.fullInternalInterfaceName})${containingProperty.name}${prefix}).${getVirtualPropertyName(virtualProperty.accessViaMember)}`;
            }
        }
        //    return `/*4* ${virtualProperty.name}/${virtualProperty.accessViaMember?.name}/${virtualProperty.accessViaProperty?.name} */${getVirtualPropertyName(virtualProperty.accessViaMember) || '/*!!*/' + virtualProperty.name}`;
        return `${getVirtualPropertyName(virtualProperty.accessViaMember) || virtualProperty.name}`;
    }
    createProperties() {
        // generate a protected backing field for each
        // and then expand the nested properties into this class forwarding to the member.  
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
        // add properties
        if ((_a = this.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.virtualProperties) {
            /* Owned Properties */
            for (const virtualProperty of (0, linq_1.values)((this.schema.language.csharp.virtualProperties.owned))) {
                let isPolymorphic = false;
                if (virtualProperty.private && virtualProperty.property.schema.type === codemodel_1.SchemaType.Object && virtualProperty.property.schema.discriminator) {
                    isPolymorphic = true;
                }
                const actualProperty = virtualProperty.property;
                let n = 0;
                const decl = this.state.project.modelsNamespace.NewResolveTypeDeclaration(actualProperty.schema, (_b = actualProperty.language.csharp) === null || _b === void 0 ? void 0 : _b.required, this.state.path('schema'));
                /* public property */
                const myProperty = new property_1.ModelProperty(virtualProperty.name, actualProperty.schema, (_c = actualProperty.language.csharp) === null || _c === void 0 ? void 0 : _c.required, actualProperty.serializedName, ((_d = actualProperty.language.csharp) === null || _d === void 0 ? void 0 : _d.description) || '', this.state.path('properties', n++), {
                    initializer: ((_e = actualProperty.language.csharp) === null || _e === void 0 ? void 0 : _e.constantValue) ? typeof actualProperty.language.csharp.constantValue === 'string' ? new codegen_csharp_1.StringExpression(actualProperty.language.csharp.constantValue) : new codegen_csharp_1.LiteralExpression(actualProperty.language.csharp.constantValue) : undefined
                });
                if (((_f = actualProperty.language.csharp) === null || _f === void 0 ? void 0 : _f.readOnly) || actualProperty.readOnly) {
                    myProperty.set = undefined;
                }
                myProperty.language = virtualProperty.property.language;
                if (((_g = actualProperty.language.csharp) === null || _g === void 0 ? void 0 : _g.constantValue) !== undefined) {
                    myProperty.setAccess = codegen_csharp_1.Access.Internal;
                    myProperty.set = undefined;
                }
                if (virtualProperty.private && !isPolymorphic) {
                    // when properties are inlined, the container accessor can be internalized. I think.
                    myProperty.setAccess = codegen_csharp_1.Access.Internal;
                    myProperty.getAccess = codegen_csharp_1.Access.Internal;
                }
                if (virtualProperty.private) {
                    this.pMap.set(virtualProperty, myProperty);
                }
                this.ownedProperties.push(this.add(myProperty));
                if (myProperty.getAccess !== codegen_csharp_1.Access.Public || myProperty.setAccess !== codegen_csharp_1.Access.Public || myProperty.set === undefined || isPolymorphic) {
                    /* internal interface property */
                    this.add(new codegen_csharp_1.Property(`${(_h = virtualProperty.originalContainingSchema.language.csharp) === null || _h === void 0 ? void 0 : _h.internalInterfaceImplementation.fullName}.${virtualProperty.name}`, decl, {
                        description: `Internal Acessors for ${virtualProperty.name}`,
                        getAccess: codegen_csharp_1.Access.Explicit,
                        setAccess: codegen_csharp_1.Access.Explicit,
                        get: myProperty.get,
                        set: myProperty.assignPrivate('value')
                    }));
                }
                myProperty.add(new codegen_csharp_1.Attribute(csharp_declarations_1.PropertyOriginAttribute, { parameters: [`${this.state.project.serviceNamespace}.PropertyOrigin.Owned`] }));
                this.addFormatAttributesToProperty(myProperty, virtualProperty);
            }
            /* Inherited properties. */
            for (const virtualProperty of (0, linq_1.values)((this.schema.language.csharp.virtualProperties.inherited))) {
                let isPolymorphic = false;
                if (virtualProperty.private && virtualProperty.property.schema.type === codemodel_1.SchemaType.Object && virtualProperty.property.schema.discriminator) {
                    isPolymorphic = true;
                }
                // so each parent property that is getting exposed
                // has to be accessed via the field in this.backingFields
                const parentField = this.backingFields.find(each => { var _a; return virtualProperty.accessViaSchema ? ((_a = virtualProperty.accessViaSchema.language.csharp) === null || _a === void 0 ? void 0 : _a.interfaceImplementation.fullName) === each.typeDeclaration.declaration : false; });
                const propertyType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(virtualProperty.property.schema, !!virtualProperty.required, this.state);
                const requiredPropertyType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(virtualProperty.property.schema, true, this.state);
                const opsType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(virtualProperty.originalContainingSchema, false, this.state);
                const via = virtualProperty.accessViaProperty;
                const parentCast = `(${(_j = virtualProperty.originalContainingSchema.language.csharp) === null || _j === void 0 ? void 0 : _j.internalInterfaceImplementation.fullName})`;
                let getFunc = (0, codegen_csharp_1.toExpression)(`(${parentCast}${parentField.field.name}).${this.accessor(virtualProperty)}`);
                let setFunc = (((_k = virtualProperty.property.language.csharp) === null || _k === void 0 ? void 0 : _k.readOnly) || ((_l = virtualProperty.property.language.csharp) === null || _l === void 0 ? void 0 : _l.constantValue)) ? undefined : (0, codegen_csharp_1.toExpression)(`(${parentCast}${parentField.field.name}).${this.accessor(virtualProperty)} = value ${virtualProperty.required ? '' : ` ?? ${requiredPropertyType.defaultOfType}`}`);
                let isConstant = false;
                if (virtualProperty.property.isDiscriminator && this.schema.discriminatorValue) {
                    for (const parent of (0, linq_1.values)((_m = this.schema.parents) === null || _m === void 0 ? void 0 : _m.all)) {
                        if (((_o = parent.discriminator) === null || _o === void 0 ? void 0 : _o.property) === virtualProperty.property) {
                            getFunc = (0, codegen_csharp_1.toExpression)(`"${this.schema.discriminatorValue}"`);
                            setFunc = (0, codegen_csharp_1.toExpression)(`(${parentCast}${parentField.field.name}).${this.accessor(virtualProperty)} = "${this.schema.discriminatorValue}"`);
                            isConstant = true;
                            if (((_p = virtualProperty.property.language.csharp) === null || _p === void 0 ? void 0 : _p.constantValue) === undefined && !virtualProperty.property.readOnly) {
                                (_q = this.constructorMethod) === null || _q === void 0 ? void 0 : _q.add(`this.${parentField.field.name}.${this.accessor(virtualProperty)} = "${this.schema.discriminatorValue}";`);
                            }
                            break;
                        }
                    }
                }
                const vp = this.add(new codegen_csharp_1.Property(virtualProperty.name, propertyType, {
                    description: (_r = virtualProperty.property.language.csharp) === null || _r === void 0 ? void 0 : _r.description,
                    get: getFunc,
                    set: setFunc
                }));
                if (((_s = virtualProperty.property.language.csharp) === null || _s === void 0 ? void 0 : _s.constantValue) !== undefined) {
                    vp.setAccess = codegen_csharp_1.Access.Internal;
                    vp.set = undefined;
                }
                if (virtualProperty.private && !isPolymorphic) {
                    vp.setAccess = codegen_csharp_1.Access.Internal;
                    vp.getAccess = codegen_csharp_1.Access.Internal;
                }
                if (vp.getAccess !== codegen_csharp_1.Access.Public || vp.setAccess !== codegen_csharp_1.Access.Public || vp.set === undefined || isPolymorphic) {
                    this.add(new codegen_csharp_1.Property(`${(_t = virtualProperty.originalContainingSchema.language.csharp) === null || _t === void 0 ? void 0 : _t.internalInterfaceImplementation.fullName}.${virtualProperty.name}`, propertyType, {
                        description: `Internal Acessors for ${virtualProperty.name}`,
                        getAccess: codegen_csharp_1.Access.Explicit,
                        setAccess: codegen_csharp_1.Access.Explicit,
                        get: (0, codegen_csharp_1.toExpression)(`(${parentCast}${parentField.field.name}).${via.name}`),
                        set: (0, codegen_csharp_1.toExpression)(`(${parentCast}${parentField.field.name}).${via.name} = value`)
                    }));
                }
                if (isConstant) {
                    vp.add(new codegen_csharp_1.Attribute(csharp_declarations_1.ConstantAttribute));
                }
                vp.add(new codegen_csharp_1.Attribute(csharp_declarations_1.PropertyOriginAttribute, { parameters: [`${this.state.project.serviceNamespace}.PropertyOrigin.Inherited`] }));
                this.addFormatAttributesToProperty(vp, virtualProperty);
            }
            /* Inlined properties. */
            for (const virtualProperty of (0, linq_1.values)(this.schema.language.csharp.virtualProperties.inlined)) {
                let isPolymorphic = false;
                if (virtualProperty.private && virtualProperty.property.schema.type === codemodel_1.SchemaType.Object && virtualProperty.property.schema.discriminator) {
                    // continue;
                    // can't remove it, it has to be either public or internally implemented.
                    isPolymorphic = true;
                }
                if (virtualProperty.accessViaProperty) {
                    const containingProperty = this.pMap.get(virtualProperty.accessViaProperty);
                    if (containingProperty) {
                        const propertyType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(virtualProperty.property.schema, !!virtualProperty.required, this.state);
                        const requiredPropertyType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(virtualProperty.property.schema, true, this.state);
                        // regular inlined property
                        const vp = new codegen_csharp_1.Property(virtualProperty.name, propertyType, {
                            description: (_u = virtualProperty.property.language.csharp) === null || _u === void 0 ? void 0 : _u.description,
                            get: (0, codegen_csharp_1.toExpression)(`${this.accessor(virtualProperty)}`),
                            set: (((_v = virtualProperty.property.language.csharp) === null || _v === void 0 ? void 0 : _v.readOnly) || ((_w = virtualProperty.property.language.csharp) === null || _w === void 0 ? void 0 : _w.constantValue)) ? undefined : (0, codegen_csharp_1.toExpression)(`${this.accessor(virtualProperty)} = value ${virtualProperty.required ? '' : ` ?? ${requiredPropertyType.defaultOfType}`}`)
                        });
                        if (!virtualProperty.private || isPolymorphic) {
                            this.add(vp);
                        }
                        if (virtualProperty.private || vp.getAccess !== codegen_csharp_1.Access.Public || vp.setAccess !== codegen_csharp_1.Access.Public || vp.set === undefined || isPolymorphic) {
                            this.add(new codegen_csharp_1.Property(`${(_x = virtualProperty.originalContainingSchema.language.csharp) === null || _x === void 0 ? void 0 : _x.internalInterfaceImplementation.fullName}.${virtualProperty.name}`, propertyType, {
                                description: `Internal Acessors for ${virtualProperty.name}`,
                                getAccess: codegen_csharp_1.Access.Explicit,
                                setAccess: codegen_csharp_1.Access.Explicit,
                                get: vp.get,
                                set: (0, codegen_csharp_1.toExpression)(`${this.accessor(virtualProperty)} = value`)
                            }));
                        }
                        if (((_y = virtualProperty.property.language.csharp) === null || _y === void 0 ? void 0 : _y.constantValue) !== undefined) {
                            vp.setAccess = codegen_csharp_1.Access.Internal;
                            vp.set = undefined;
                        }
                        vp.add(new codegen_csharp_1.Attribute(csharp_declarations_1.PropertyOriginAttribute, { parameters: [`${this.state.project.serviceNamespace}.PropertyOrigin.Inlined`] }));
                        this.addFormatAttributesToProperty(vp, virtualProperty);
                    }
                }
            }
            /* Appended properties. */
            if (this.state.project.resourceGroupAppend && this.state.project.azure && this.schema.extensions && this.schema.extensions['is-return-object']) {
                this.appendResourceGroupName();
            }
        }
    }
    addFormatAttributesToProperty(property, virtualProperty) {
        if (virtualProperty.format) {
            if (virtualProperty.format.suppressFormat) {
                property.add(new codegen_csharp_1.Attribute(csharp_declarations_1.DoNotFormatAttribute));
            }
            else {
                const parameters = [];
                if (virtualProperty.format.index !== undefined) {
                    parameters.push(`Index = ${virtualProperty.format.index}`);
                }
                if (virtualProperty.format.label !== undefined) {
                    parameters.push(`Label = ${new codegen_csharp_1.StringExpression(virtualProperty.format.label)}`);
                }
                if (virtualProperty.format.width !== undefined) {
                    parameters.push(`Width = ${virtualProperty.format.width}`);
                }
                property.add(new codegen_csharp_1.Attribute(csharp_declarations_1.FormatTableAttribute, { parameters }));
            }
        }
    }
    addValidation() {
        if (this.validationStatements.implementation.trim()) {
            // we do have something to valdiate!
            // add the IValidates implementation to this object.
            this.interfaces.push(clientruntime_1.ClientRuntime.IValidates);
            this.validateMethod = this.addMethod(new codegen_csharp_1.Method('Validate', codegen_csharp_1.System.Threading.Tasks.Task(), {
                async: codegen_csharp_1.Modifier.Async,
                parameters: [this.validationEventListener],
                description: 'Validates that this object meets the validation criteria.',
                returnsDescription: `A <see cref = "${codegen_csharp_1.System.Threading.Tasks.Task()}" /> that will be complete when validation is completed.`
            }));
            this.validateMethod.add(this.validationStatements);
        }
    }
    additionalPropertiesType(aSchema) {
        var _a, _b, _c;
        const schema = aSchema.type === codemodel_1.SchemaType.Dictionary ? aSchema :
            aSchema.type === codemodel_1.SchemaType.Object ? (_b = (_a = aSchema.parents) === null || _a === void 0 ? void 0 : _a.immediate) === null || _b === void 0 ? void 0 : _b.find((s) => s.type === codemodel_1.SchemaType.Dictionary) :
                undefined;
        if (schema) {
            const dictSchema = schema;
            if (dictSchema.elementType.type === codemodel_1.SchemaType.Any) {
                return codegen_csharp_1.System.Object;
            }
            else {
                // we're going to implement IDictionary<string, schema.additionalProperties>
                return this.state.project.modelsNamespace.NewResolveTypeDeclaration(dictSchema.elementType, true, this.state);
            }
        }
        else
            for (const each of (0, linq_1.values)((_c = aSchema.parents) === null || _c === void 0 ? void 0 : _c.immediate)) {
                const r = this.additionalPropertiesType(each);
                if (r) {
                    return r;
                }
            }
        return undefined;
    }
    handleAllOf() {
        var _a, _b, _c, _d;
        let hasAdditionalPropertiesInParent = false;
        // handle <allOf>s
        // add an 'implements' for the interface for the allOf.
        for (const { key: eachSchemaIndex, value: eachSchemaValue } of (0, linq_1.items)((_a = this.schema.parents) === null || _a === void 0 ? void 0 : _a.immediate)) {
            if (eachSchemaValue.type === codemodel_1.SchemaType.Dictionary) {
                continue;
            }
            const aSchema = eachSchemaValue;
            const aState = this.state.path('allOf', eachSchemaIndex);
            const td = this.state.project.modelsNamespace.NewResolveTypeDeclaration(aSchema, true, aState);
            const parentClass = (_b = aSchema.language.csharp) === null || _b === void 0 ? void 0 : _b.classImplementation;
            const className = parentClass.fullName;
            const fieldName = (0, codegen_1.camelCase)((0, codegen_1.deconstruct)(className.replace(/^.*\./, '')));
            // add the interface as a parent to our interface.
            const iface = (_c = aSchema.language.csharp) === null || _c === void 0 ? void 0 : _c.interfaceImplementation;
            // add a field for the inherited values
            const backingField = this.addField(new codegen_csharp_1.Field(`__${fieldName}`, td, { initialValue: `new ${className}()`, access: codegen_csharp_1.Access.Private, description: `Backing field for Inherited model <see cref= "${td.declaration}" /> ` }));
            this.backingFields.push({
                className,
                typeDeclaration: td,
                field: backingField
            });
            this.validationStatements.add(td.validatePresence(this.validationEventListener, backingField));
            this.validationStatements.add(td.validateValue(this.validationEventListener, backingField));
            this.internalModelInterface.interfaces.push((_d = aSchema.language.csharp) === null || _d === void 0 ? void 0 : _d.internalInterfaceImplementation);
            this.modelInterface.interfaces.push(iface);
            //
            const addlPropType = this.additionalPropertiesType(aSchema);
            if (addlPropType) {
                this.dictionaryImpl = new model_class_dictionary_1.DictionaryImplementation(this).init(addlPropType, backingField);
                hasAdditionalPropertiesInParent = true;
            }
        }
        return hasAdditionalPropertiesInParent;
    }
    handleDiscriminator() {
        var _a, _b;
        if (this.schema.discriminator) {
            // this has a discriminator property.
            // our children are expected to tell us who they are
            this.isPolymorphic = true;
            // we'll add a deserializer factory method a bit later..
        }
        if (this.schema.discriminatorValue) {
            // we have a discriminator value, and we should tell our parent who we are so that they can build a proper deserializer method.
            // um. just how do we *really* know which allOf is polymorphic?
            // that's really sad.
            for (const { key: eachAllOfIndex, value: eachAllOfValue } of (0, linq_1.items)((_a = this.schema.parents) === null || _a === void 0 ? void 0 : _a.all)) {
                const parentSchema = eachAllOfValue;
                const aState = this.state.path('allOf', eachAllOfIndex);
                // ensure the parent schema has it's class created first.
                this.state.project.modelsNamespace.NewResolveTypeDeclaration(parentSchema, true, aState);
                const parentClass = (_b = parentSchema.language.csharp) === null || _b === void 0 ? void 0 : _b.classImplementation;
                if (!!parentClass && parentClass.isPolymorphic) {
                    // remember this class for later.
                    this.parentModelClasses.push(parentClass);
                    // tell that parent who we are.
                    parentClass.addDiscriminator(this.schema.discriminatorValue, this);
                }
            }
        }
    }
    addHeaderDeserializer() {
        var _a, _b;
        const avp = (0, schema_1.getAllVirtualProperties)((_a = this.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.virtualProperties);
        const headers = new codegen_csharp_1.Parameter('headers', codegen_csharp_1.System.Net.Http.Headers.HttpResponseHeaders);
        const readHeaders = new codegen_csharp_1.Method(`${clientruntime_1.ClientRuntime.IHeaderSerializable}.ReadHeaders`, undefined, {
            access: codegen_csharp_1.Access.Explicit,
            parameters: [headers],
        });
        let used = false;
        let i = 0;
        for (const headerProperty of (0, linq_1.values)(avp).where(each => { var _a, _b; return ((_a = each.property.language.csharp) === null || _a === void 0 ? void 0 : _a[codemodel_v3_1.HeaderProperty]) === codemodel_v3_1.HeaderPropertyType.HeaderAndBody || ((_b = each.property.language.csharp) === null || _b === void 0 ? void 0 : _b[codemodel_v3_1.HeaderProperty]) === codemodel_v3_1.HeaderPropertyType.Header; })) {
            used = true;
            const t = `((${(_b = headerProperty.originalContainingSchema.language.csharp) === null || _b === void 0 ? void 0 : _b.fullInternalInterfaceName})this)`;
            const values = `__${(0, codegen_1.camelCase)([...(0, codegen_1.deconstruct)(headerProperty.property.serializedName), 'Header'])}`;
            const td = this.state.project.modelsNamespace.NewResolveTypeDeclaration(headerProperty.property.schema, false, this.state);
            readHeaders.add((0, codegen_csharp_1.If)(`${(0, codegen_csharp_1.valueOf)(headers)}.TryGetValues("${headerProperty.property.serializedName}", out var ${values}${i})`, `${t}.${headerProperty.name} = ${td.deserializeFromContainerMember(codemodel_v3_1.KnownMediaType.Header, headers, values + i, td.defaultOfType)};`));
            i++;
        }
        if (used) {
            this.interfaces.push(clientruntime_1.ClientRuntime.IHeaderSerializable);
            this.add(readHeaders);
        }
    }
    appendResourceGroupName() {
        var _a, _b;
        const virtualProperties = (0, schema_1.getAllVirtualProperties)((_a = this.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.virtualProperties);
        const idProperties = (0, linq_1.values)(virtualProperties).where(each => each.name === 'Id').toArray();
        const resourceGroupNameProperties = (0, linq_1.values)(virtualProperties).where(each => each.name === 'ResourceGroupName').toArray();
        if (idProperties.length == 1 && resourceGroupNameProperties.length === 0) {
            const idProperty = idProperties[0];
            const resourceGroupNamePropertyName = (0, codegen_1.getPascalIdentifier)('ResourceGroupName');
            const resourceGroupNameDescription = 'Gets the resource group name';
            const actualResourceGroupProperty = idProperty.property;
            actualResourceGroupProperty.serializedName = resourceGroupNamePropertyName;
            const decl = this.state.project.modelsNamespace.NewResolveTypeDeclaration(actualResourceGroupProperty.schema, false, this.state.path('schema'));
            const resourceGroupNameProperty = new codegen_csharp_1.Property(`${resourceGroupNamePropertyName}`, decl, {
                description: resourceGroupNameDescription,
                getAccess: codegen_csharp_1.Access.Public,
                setAccess: codegen_csharp_1.Access.Public,
                get: (0, codegen_csharp_1.toExpression)('(new global::System.Text.RegularExpressions.Regex("^/subscriptions/(?<subscriptionId>[^/]+)/resourceGroups/(?<resourceGroupName>[^/]+)/providers/", global::System.Text.RegularExpressions.RegexOptions.IgnoreCase).Match(this.Id).Success ? new global::System.Text.RegularExpressions.Regex("^/subscriptions/(?<subscriptionId>[^/]+)/resourceGroups/(?<resourceGroupName>[^/]+)/providers/", global::System.Text.RegularExpressions.RegexOptions.IgnoreCase).Match(this.Id).Groups["resourceGroupName"].Value : null)')
            });
            const format = this.state.project.formats[`${(_b = this.schema.language.csharp) === null || _b === void 0 ? void 0 : _b.name}`];
            const virtualResourceGroupNameProperty = {
                name: resourceGroupNamePropertyName,
                property: actualResourceGroupProperty,
                private: false,
                nameComponents: [resourceGroupNamePropertyName],
                nameOptions: [resourceGroupNamePropertyName],
                description: resourceGroupNameDescription,
                originalContainingSchema: actualResourceGroupProperty.schema,
                alias: [],
                required: false,
                format: format
            };
            resourceGroupNameProperty.add(new codegen_csharp_1.Attribute(csharp_declarations_1.PropertyOriginAttribute, { parameters: [`${this.state.project.serviceNamespace}.PropertyOrigin.Owned`] }));
            this.addFormatAttributesToProperty(resourceGroupNameProperty, virtualResourceGroupNameProperty);
            this.add(resourceGroupNameProperty);
        }
    }
    validateValue(eventListener, property) {
        return this.featureImplementation.validateValue(eventListener, property);
    }
    validatePresence(eventListener, property) {
        return this.featureImplementation.validatePresence(eventListener, property);
    }
    addDiscriminator(discriminatorValue, modelClass) {
        this.discriminators.set(discriminatorValue, modelClass);
        // tell any polymorphic parents incase we're doing subclass of a subclass.
        for (const each of this.parentModelClasses) {
            each.addDiscriminator(discriminatorValue, modelClass);
        }
    }
}
exports.ModelClass = ModelClass;
//# sourceMappingURL=model-class.js.map