"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeserializerPartialClass = exports.SerializationPartialClass = void 0;
const codegen_1 = require("@azure-tools/codegen");
const linq_1 = require("@azure-tools/linq");
const codegen_csharp_1 = require("@azure-tools/codegen-csharp");
const codegen_csharp_2 = require("@azure-tools/codegen-csharp");
const codegen_csharp_3 = require("@azure-tools/codegen-csharp");
const codegen_csharp_4 = require("@azure-tools/codegen-csharp");
const codegen_csharp_5 = require("@azure-tools/codegen-csharp");
const codegen_csharp_6 = require("@azure-tools/codegen-csharp");
const codegen_csharp_7 = require("@azure-tools/codegen-csharp");
const codegen_csharp_8 = require("@azure-tools/codegen-csharp");
const codemodel_1 = require("@autorest/codemodel");
const model_class_1 = require("./model-class");
class SerializationPartialClass extends codegen_1.Initializer {
    constructor(targetClass, targetInterface, serializationType, serializationFormat, schema, resolver, objectInitializer) {
        super();
        this.targetClass = targetClass;
        this.targetInterface = targetInterface;
        this.serializationType = serializationType;
        this.serializationFormat = serializationFormat;
        this.schema = schema;
        this.resolver = resolver;
        this.contentParameter = new codegen_csharp_4.Parameter('content', this.serializationType, { description: `The ${this.serializationType.declaration} content that should be used.` });
        this.refContainerParameter = new codegen_csharp_4.Parameter('container', this.serializationType, { modifier: codegen_csharp_5.ParameterModifier.Ref, description: `The ${this.serializationType.declaration} container that the serialization result will be placed in.` });
        this.returnNowParameter = new codegen_csharp_4.Parameter('returnNow', codegen_csharp_8.dotnet.Bool, { modifier: codegen_csharp_5.ParameterModifier.Ref, description: 'Determines if the rest of the serialization should be processed, or if the method should return instantly.' });
        this.apply(objectInitializer);
    }
    get virtualProperties() {
        var _a;
        return ((_a = this.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.virtualProperties) || {
            owned: [],
            inherited: [],
            inlined: []
        };
    }
    get allVirtualProperties() {
        const vp = this.virtualProperties;
        // return [...vp.owned, ...vp.inherited, ...vp.inlined];
        return (0, linq_1.values)(vp.owned, vp.inherited, vp.inlined).toArray();
    }
    get typeCref() {
        return `<see cref="${this.serializationType.declaration}" />`;
    }
    get thisCref() {
        return `<see cref="${this.targetClass.declaration}" />`;
    }
    get interfaceCref() {
        return `<see cref="${this.targetInterface.declaration}" />`;
    }
}
exports.SerializationPartialClass = SerializationPartialClass;
class DeserializerPartialClass extends SerializationPartialClass {
    constructor(targetClass, targetInterface, serializationType, serializationFormat, schema, resolver, objectInitializer) {
        super(targetClass, targetInterface, serializationType, serializationFormat, schema, resolver);
        this.serializationType = serializationType;
        this.serializationFormat = serializationFormat;
        this.schema = schema;
        this.apply(objectInitializer);
    }
    async init() {
        // add partial methods for extensibility
        this.addPartialMethods();
        this.addDeserializerConstructor();
        this.addDeserializerMethod();
    }
    addDeserializerConstructor() {
        const $this = this;
        const deserializerConstructor = this.targetClass.addMethod(new codegen_csharp_2.Constructor(this.targetClass, {
            parameters: [this.contentParameter], access: codegen_csharp_1.Access.Internal,
            description: `Deserializes a ${this.typeCref} into a new instance of ${this.thisCref}.`
        }));
        deserializerConstructor.add(function* () {
            const returnNow = new codegen_csharp_1.LocalVariable('returnNow', codegen_csharp_8.dotnet.Bool, { initializer: codegen_csharp_8.dotnet.False });
            yield returnNow.declarationStatement;
            yield `${$this.beforeDeserialize.name}(${$this.contentParameter}, ref ${returnNow.value});`;
            yield (0, codegen_csharp_6.If)(returnNow, 'return;');
            yield $this.deserializeStatements;
            if ($this.hasAadditionalProperties($this.schema)) {
                // this type has an additional properties dictionary
                yield '// this type is a dictionary; copy elements from source to here.';
                yield `CopyFrom(${$this.contentParameter.value});`;
            }
            yield `${$this.afterDeserialize.name}(${$this.contentParameter});`;
        });
    }
    hasAadditionalProperties(aSchema) {
        var _a;
        if (aSchema.type === codemodel_1.SchemaType.Dictionary) {
            return true;
        }
        if (aSchema.type !== codemodel_1.SchemaType.Object) {
            return false;
        }
        const objSchema = (_a = aSchema.parents) === null || _a === void 0 ? void 0 : _a.immediate;
        if (!objSchema || objSchema.length === 0) {
            return false;
        }
        for (const parent of objSchema) {
            if (this.hasAadditionalProperties(parent)) {
                return true;
            }
        }
        return false;
    }
    get deserializeStatements() {
        const $this = this;
        return function* () {
            var _a;
            yield '// actually deserialize ';
            for (const virtualProperty of (0, linq_1.values)($this.allVirtualProperties)) {
                // yield `// deserialize ${virtualProperty.name} from ${$this.serializationFormat}`;
                const type = $this.resolver(virtualProperty.property.schema, virtualProperty.property.language.default.required);
                const cvt = type.convertObjectMethod;
                const t = `((${(_a = virtualProperty.originalContainingSchema.language.csharp) === null || _a === void 0 ? void 0 : _a.fullInternalInterfaceName})this)`;
                const tt = type ? `(${type.declaration})` : '';
                yield (0, codegen_csharp_6.If)(`content.Contains("${(0, model_class_1.getVirtualPropertyName)(virtualProperty)}")`, `${t}.${(0, model_class_1.getVirtualPropertyName)(virtualProperty)} = ${tt} ${$this.contentParameter}.GetValueForProperty("${(0, model_class_1.getVirtualPropertyName)(virtualProperty)}",${t}.${(0, model_class_1.getVirtualPropertyName)(virtualProperty)}, ${cvt});`);
            }
        };
    }
    addDeserializerMethod() {
        const $this = this;
        const deserialzeMethod = this.targetClass.addMethod(new codegen_csharp_3.Method(`DeserializeFrom${this.serializationFormat}`, this.targetInterface, {
            parameters: [this.contentParameter], static: codegen_csharp_1.Modifier.Static,
            description: `Deserializes a ${this.typeCref} into an instance of ${this.thisCref}.`,
            returnsDescription: `an instance of ${this.interfaceCref}.`
        }));
        deserialzeMethod.add(function* () {
            yield (0, codegen_csharp_7.Return)($this.targetClass.new($this.contentParameter));
        });
    }
    addPartialMethods() {
        const before = `BeforeDeserialize${this.serializationFormat}`;
        const after = `AfterDeserialize${this.serializationFormat}`;
        this.beforeDeserialize = this.targetClass.addMethod(new codegen_csharp_3.PartialMethod(before, codegen_csharp_8.dotnet.Void, {
            access: codegen_csharp_1.Access.Default,
            parameters: [this.contentParameter, this.returnNowParameter],
            description: `<c>${before}</c> will be called before the deserialization has commenced, allowing complete customization of the object before it is deserialized.
      If you wish to disable the default deserialization entirely, return <c>true</c> in the <paramref name="returnNow" /> output parameter.
      Implement this method in a partial class to enable this behavior.`
        }));
        this.afterDeserialize = this.targetClass.addMethod(new codegen_csharp_3.PartialMethod(after, codegen_csharp_8.dotnet.Void, {
            access: codegen_csharp_1.Access.Default,
            parameters: [this.contentParameter],
            description: `<c>${after}</c> will be called after the deserialization has finished, allowing customization of the object before it is returned. Implement this method in a partial class to enable this behavior `
        }));
    }
}
exports.DeserializerPartialClass = DeserializerPartialClass;
//# sourceMappingURL=model-class-serializer.js.map