"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModelExtensionsNamespace = void 0;
const linq_1 = require("@azure-tools/linq");
const codegen_csharp_1 = require("@azure-tools/codegen-csharp");
const exports_1 = require("../llcsharp/exports");
const powershell_declarations_1 = require("../internal/powershell-declarations");
const path_1 = require("path");
class ApiVersionModelExtensionsNamespace extends codegen_csharp_1.Namespace {
    get outputFolder() {
        return `${this.baseFolder}/${this.apiVersion.replace(/.*\./g, '')}`;
    }
    constructor(baseFolder, apiVersion, objectInitializer) {
        super(apiVersion);
        this.baseFolder = baseFolder;
        this.apiVersion = apiVersion;
        this.apply(objectInitializer);
        this.add(new codegen_csharp_1.ImportDirective(`${exports_1.ClientRuntime.name}.PowerShell`));
    }
}
class ModelExtensionsNamespace extends codegen_csharp_1.Namespace {
    CreateReferenceType() {
        const rt = new codegen_csharp_1.Class(this, 'ReferenceType');
        rt.add(new codegen_csharp_1.Property('Id', codegen_csharp_1.dotnet.String, { setAccess: codegen_csharp_1.Access.Internal }));
        return rt;
    }
    get outputFolder() {
        return (0, path_1.join)(this.state.project.apiFolder, 'Models');
    }
    constructor(parent, schemas, state, objectInitializer) {
        var _a, _b, _c, _d, _e, _f, _g;
        super('Models', parent);
        this.schemas = schemas;
        this.state = state;
        this.subNamespaces = new linq_1.Dictionary();
        this.resolver = new exports_1.SchemaDefinitionResolver(this.state.project.fixedArray);
        this.apply(objectInitializer);
        this.add(new codegen_csharp_1.ImportDirective(`${exports_1.ClientRuntime.name}.PowerShell`));
        this.subNamespaces[this.fullName] = this;
        const $this = this;
        const resolver = (s, req) => this.resolver.resolveTypeDeclaration(s, req, state, state.project.fixedArray);
        // Add typeconverters to model classes (partial)
        for (const schemaGroup of (0, linq_1.values)(schemas)) {
            for (const schema of (0, linq_1.values)(schemaGroup)) {
                if (!schema || (schema.language.csharp && schema.language.csharp.skip)) {
                    continue;
                }
                const td = this.resolver.resolveTypeDeclaration(schema, true, state, state.project.fixedArray);
                if (td instanceof exports_1.ObjectImplementation) {
                    // it's a class object.
                    const className = ((_a = td.schema.language.csharp) === null || _a === void 0 ? void 0 : _a.name) || '';
                    const interfaceName = ((_b = td.schema.language.csharp) === null || _b === void 0 ? void 0 : _b.interfaceName) || '';
                    const converterClass = `${className}TypeConverter`;
                    if (this.findClassByName(className).length > 0) {
                        continue;
                    }
                    // get the actual full namespace for the schema
                    const fullname = ((_c = schema.language.csharp) === null || _c === void 0 ? void 0 : _c.namespace) || this.fullName;
                    const ns = this.subNamespaces[fullname] || this.add(new ApiVersionModelExtensionsNamespace(this.outputFolder, fullname));
                    ns.header = this.state.project.license;
                    // create the model extensions for each object model
                    // 2. A partial interface with the type converter attribute
                    const modelInterface = new codegen_csharp_1.Interface(ns, interfaceName, {
                        partial: true,
                        description: (_d = td.schema.language.csharp) === null || _d === void 0 ? void 0 : _d.description,
                        fileName: `${interfaceName}.PowerShell` // make sure that the interface ends up in the same file as the class.
                    });
                    modelInterface.add(new codegen_csharp_1.Attribute(powershell_declarations_1.TypeConverterAttribute, { parameters: [new codegen_csharp_1.LiteralExpression(`typeof(${converterClass})`)] }));
                    // 1. A partial class with the type converter attribute
                    const model = new codegen_csharp_1.Class(ns, className, undefined, {
                        partial: true,
                        description: (_e = td.schema.language.csharp) === null || _e === void 0 ? void 0 : _e.description,
                        fileName: `${className}.PowerShell`
                    });
                    // if the model is supposed to be use 'by-reference' we should create an I*Reference interface for that
                    // and add that interface to the extension class
                    if ((_f = schema.language.csharp) === null || _f === void 0 ? void 0 : _f.byReference) {
                        const refInterface = `${interfaceName}_Reference`;
                        schema.language.csharp.referenceInterface = `${ns.fullName}.${refInterface}`;
                        const referenceInterface = new codegen_csharp_1.Interface(ns, refInterface, {
                            partial: true,
                            description: `Reference for model ${fullname}`,
                            fileName: `${interfaceName}.PowerShell` // make sure that the interface ends up in the same file as the class.
                        });
                        referenceInterface.add(new codegen_csharp_1.Attribute(powershell_declarations_1.TypeConverterAttribute, { parameters: [new codegen_csharp_1.LiteralExpression(`typeof(${converterClass})`)] }));
                        referenceInterface.add(new codegen_csharp_1.InterfaceProperty('Id', codegen_csharp_1.dotnet.String, { setAccess: codegen_csharp_1.Access.Internal }));
                        model.interfaces.push(referenceInterface);
                        // add it to the generic reference type.
                        // referenceType = referenceType || this.CreateReferenceType();
                        // referenceType.interfaces.push(referenceInterface);
                    }
                    model.add(new codegen_csharp_1.Attribute(powershell_declarations_1.TypeConverterAttribute, { parameters: [new codegen_csharp_1.LiteralExpression(`typeof(${converterClass})`)] }));
                    model.add(new codegen_csharp_1.LambdaMethod('FromJsonString', modelInterface, new codegen_csharp_1.LiteralExpression(`FromJson(${exports_1.ClientRuntime.JsonNode.declaration}.Parse(jsonText))`), {
                        static: codegen_csharp_1.Modifier.Static,
                        parameters: [new codegen_csharp_1.Parameter('jsonText', codegen_csharp_1.dotnet.String, { description: 'a string containing a JSON serialized instance of this model.' })],
                        description: `Creates a new instance of <see cref="${(_g = td.schema.language.csharp) === null || _g === void 0 ? void 0 : _g.name}" />, deserializing the content from a json string.`,
                        returnsDescription: `an instance of the <see cref="${className}" /> model class.`
                    }));
                    model.add(new codegen_csharp_1.LambdaMethod('ToJsonString', codegen_csharp_1.dotnet.String, new codegen_csharp_1.LiteralExpression(`ToJson(${codegen_csharp_1.dotnet.Null}, ${exports_1.ClientRuntime.SerializationMode.IncludeAll})?.ToString()`), {
                        description: 'Serializes this instance to a json string.',
                        returnsDescription: 'a <see cref="System.String" /> containing this model serialized to JSON text.'
                    }));
                    if (this.state.project.addToString) {
                        // add partial OverrideToString method
                        const returnNow = new codegen_csharp_1.Parameter('returnNow', codegen_csharp_1.dotnet.Bool, { modifier: codegen_csharp_1.ParameterModifier.Ref, description: '/// set returnNow to true if you provide a customized OverrideToString function' });
                        const stringResult = new codegen_csharp_1.Parameter('stringResult', codegen_csharp_1.dotnet.String, { modifier: codegen_csharp_1.ParameterModifier.Ref, description: '/// instance serialized to a string, normally it is a Json' });
                        const overrideToStringMethod = new codegen_csharp_1.PartialMethod('OverrideToString', codegen_csharp_1.dotnet.Void, {
                            parameters: [stringResult, returnNow],
                            description: '<c>OverrideToString</c> will be called if it is implemented. Implement this method in a partial class to enable this behavior'
                        });
                        model.add(overrideToStringMethod);
                        // add ToString method
                        const toStringMethod = new codegen_csharp_1.Method('ToString', codegen_csharp_1.dotnet.String, {
                            override: codegen_csharp_1.Modifier.Override,
                            access: codegen_csharp_1.Access.Public
                        });
                        toStringMethod.add(function* () {
                            const skip = (0, codegen_csharp_1.Local)('returnNow', `${codegen_csharp_1.dotnet.False}`);
                            const result = (0, codegen_csharp_1.Local)('result', 'global::System.String.Empty');
                            yield skip.declarationStatement;
                            yield result.declarationStatement;
                            yield `${overrideToStringMethod.invoke(`ref ${result.value}`, `ref ${skip.value}`)};`;
                            yield (0, codegen_csharp_1.If)(`${skip}`, (0, codegen_csharp_1.Return)(`${result}`));
                            yield 'return ToJsonString();';
                        });
                        model.add(toStringMethod);
                    }
                    const hashDeseralizer = new exports_1.DeserializerPartialClass(model, modelInterface, codegen_csharp_1.System.Collections.IDictionary, 'Dictionary', schema, resolver).init();
                    const psDeseralizer = new exports_1.DeserializerPartialClass(model, modelInterface, powershell_declarations_1.PSObject, 'PSObject', schema, resolver).init();
                    // + static <interfaceType> FromJsonString(string json);
                    // + string ToJsonString()
                    // 3. A TypeConverter class
                    const typeConverter = new codegen_csharp_1.Class(ns, converterClass, powershell_declarations_1.PSTypeConverter, {
                        description: `A PowerShell PSTypeConverter to support converting to an instance of <see cref="${className}" />`,
                        fileName: `${className}.TypeConverter`
                    });
                    typeConverter.add(new codegen_csharp_1.LambdaMethod('CanConvertTo', codegen_csharp_1.dotnet.Bool, codegen_csharp_1.dotnet.False, {
                        override: codegen_csharp_1.Modifier.Override,
                        parameters: [
                            new codegen_csharp_1.Parameter('sourceValue', codegen_csharp_1.dotnet.Object, { description: 'the <see cref="System.Object"/> to convert from' }),
                            new codegen_csharp_1.Parameter('destinationType', codegen_csharp_1.System.Type, { description: 'the <see cref="System.Type" /> to convert to' })
                        ],
                        description: 'Determines if the <paramref name="sourceValue" /> parameter can be converted to the <paramref name="destinationType" /> parameter',
                        returnsDescription: '<c>true</c> if the converter can convert the <paramref name="sourceValue" /> parameter to the <paramref name="destinationType" /> parameter, otherwise <c>false</c>',
                    }));
                    typeConverter.add(new codegen_csharp_1.LambdaMethod('ConvertTo', codegen_csharp_1.dotnet.Object, codegen_csharp_1.dotnet.Null, {
                        override: codegen_csharp_1.Modifier.Override,
                        parameters: [
                            new codegen_csharp_1.Parameter('sourceValue', codegen_csharp_1.dotnet.Object, { description: 'the <see cref="System.Object"/> to convert from' }),
                            new codegen_csharp_1.Parameter('destinationType', codegen_csharp_1.System.Type, { description: 'the <see cref="System.Type" /> to convert to' }),
                            new codegen_csharp_1.Parameter('formatProvider', codegen_csharp_1.System.IFormatProvider, { description: 'not used by this TypeConverter.' }),
                            new codegen_csharp_1.Parameter('ignoreCase', codegen_csharp_1.dotnet.Bool, { description: 'when set to <c>true</c>, will ignore the case when converting.' }),
                        ], description: 'NotImplemented -- this will return <c>null</c>',
                        returnsDescription: 'will always return <c>null</c>.'
                    }));
                    typeConverter.add(new codegen_csharp_1.LambdaMethod('CanConvertFrom', codegen_csharp_1.dotnet.Bool, new codegen_csharp_1.LiteralExpression('CanConvertFrom(sourceValue)'), {
                        override: codegen_csharp_1.Modifier.Override,
                        parameters: [
                            new codegen_csharp_1.Parameter('sourceValue', codegen_csharp_1.dotnet.Object, { description: 'the <see cref="System.Object"/> to convert from' }),
                            new codegen_csharp_1.Parameter('destinationType', codegen_csharp_1.System.Type, { description: 'the <see cref="System.Type" /> to convert to' })
                        ],
                        description: 'Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType" /> parameter.',
                        returnsDescription: '<c>true</c> if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType" /> parameter, otherwise <c>false</c>.',
                    }));
                    typeConverter.add(new codegen_csharp_1.LambdaMethod('ConvertFrom', codegen_csharp_1.dotnet.Object, new codegen_csharp_1.LiteralExpression('ConvertFrom(sourceValue)'), {
                        override: codegen_csharp_1.Modifier.Override,
                        parameters: [
                            new codegen_csharp_1.Parameter('sourceValue', codegen_csharp_1.dotnet.Object, { description: 'the <see cref="System.Object"/> to convert from' }),
                            new codegen_csharp_1.Parameter('destinationType', codegen_csharp_1.System.Type, { description: 'the <see cref="System.Type" /> to convert to' }),
                            new codegen_csharp_1.Parameter('formatProvider', codegen_csharp_1.System.IFormatProvider, { description: 'not used by this TypeConverter.' }),
                            new codegen_csharp_1.Parameter('ignoreCase', codegen_csharp_1.dotnet.Bool, { description: 'when set to <c>true</c>, will ignore the case when converting.' }),
                        ],
                        description: 'Converts the <paramref name="sourceValue" /> parameter to the <paramref name="destinationType" /> parameter using <paramref name="formatProvider" /> and <paramref name="ignoreCase" /> ',
                        returnsDescription: `an instance of <see cref="${className}" />, or <c>null</c> if there is no suitable conversion.`
                    }));
                    typeConverter.add(new codegen_csharp_1.Method('CanConvertFrom', codegen_csharp_1.dotnet.Bool, {
                        static: codegen_csharp_1.Modifier.Static,
                        parameters: [
                            new codegen_csharp_1.Parameter('sourceValue', codegen_csharp_1.dotnet.Dynamic, { description: `the <see cref="System.Object" /> instance to check if it can be converted to the <see cref="${className}" /> type.` }),
                        ],
                        description: `Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <see cref="${className}"/> type.`,
                        returnsDescription: `<c>true</c> if the instance could be converted to a <see cref="${className}" /> type, otherwise <c>false</c> `
                    })).add(function* () {
                        var _a;
                        yield (0, codegen_csharp_1.If)('null == sourceValue', (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.True));
                        const t = new codegen_csharp_1.LocalVariable('type', codegen_csharp_1.System.Type, { initializer: 'sourceValue.GetType()' });
                        yield t.declarationStatement;
                        if (schema.language.default.uid || ((_a = schema.language.csharp) === null || _a === void 0 ? void 0 : _a.byReference)) {
                            yield '// we allow string conversion too.';
                            yield (0, codegen_csharp_1.If)(`${t.value} == typeof(${codegen_csharp_1.System.String})`, (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.True));
                        }
                        yield (0, codegen_csharp_1.If)((0, codegen_csharp_1.IsAssignableFrom)(powershell_declarations_1.PSObject, t), function* () {
                            yield '// we say yest to PSObjects';
                            yield (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.True);
                        });
                        yield (0, codegen_csharp_1.If)((0, codegen_csharp_1.IsAssignableFrom)(codegen_csharp_1.System.Collections.IDictionary, t), function* () {
                            yield '// we say yest to Hashtables/dictionaries';
                            yield (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.True);
                        });
                        yield (0, codegen_csharp_1.Try)((0, codegen_csharp_1.If)('null != sourceValue.ToJsonString()', (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.True)));
                        yield (0, codegen_csharp_1.Catch)(undefined, '// Not one of our objects');
                        yield (0, codegen_csharp_1.Try)(function* () {
                            const t = new codegen_csharp_1.LocalVariable('text', codegen_csharp_1.dotnet.String, { initializer: 'sourceValue.ToString()?.Trim()' });
                            yield t.declarationStatement;
                            yield (0, codegen_csharp_1.Return)(`${codegen_csharp_1.dotnet.True} == ${t.value}?.StartsWith("{") && ${codegen_csharp_1.dotnet.True} == ${t.value}?.EndsWith("}") && ${exports_1.ClientRuntime.JsonNode.Parse(t)}.Type == ${exports_1.ClientRuntime.JsonType.Object}`);
                        });
                        yield (0, codegen_csharp_1.Catch)(undefined, '// Doesn\'t look like it can be treated as JSON');
                        yield (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.False);
                    });
                    typeConverter.add(new codegen_csharp_1.Method('ConvertFrom', modelInterface, {
                        static: codegen_csharp_1.Modifier.Static,
                        parameters: [
                            new codegen_csharp_1.Parameter('sourceValue', codegen_csharp_1.dotnet.Dynamic, {
                                description: `the value to convert into an instance of <see cref="${className}" />.`
                            }),
                        ],
                        description: `Converts the <paramref name="sourceValue" /> parameter into an instance of <see cref="${className}" />`,
                        returnsDescription: `an instance of <see cref="${className}" />, or <c>null</c> if there is no suitable conversion.`
                    })).add(function* () {
                        var _a, _b;
                        // null begets null
                        yield (0, codegen_csharp_1.If)('null == sourceValue', (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.Null));
                        const t = new codegen_csharp_1.LocalVariable('type', codegen_csharp_1.System.Type, { initializer: 'sourceValue.GetType()' });
                        yield t.declarationStatement;
                        if (($this.state.project.azure && schema.language.default.uid === 'universal-parameter-type') || ((_a = schema.language.csharp) === null || _a === void 0 ? void 0 : _a.byReference)) {
                            yield '// support direct string to id type conversion.';
                            yield (0, codegen_csharp_1.If)(`${t.value} == typeof(${codegen_csharp_1.System.String})`, function* () {
                                yield (0, codegen_csharp_1.Return)(`new ${className} { Id = sourceValue }`);
                            });
                        }
                        if ((_b = schema.language.csharp) === null || _b === void 0 ? void 0 : _b.byReference) {
                            yield '// if Id is present with by-reference schemas, just return the type with Id ';
                            yield (0, codegen_csharp_1.Try)((0, codegen_csharp_1.Return)(`new ${className} { Id = sourceValue.Id }`));
                            yield (0, codegen_csharp_1.Catch)(undefined, '// Not an Id reference parameter');
                        }
                        // if the type can be assigned directly, do that 
                        yield (0, codegen_csharp_1.If)((0, codegen_csharp_1.IsAssignableFrom)(td, t), (0, codegen_csharp_1.Return)('sourceValue'));
                        // try using json first (either from string or toJsonString())
                        yield (0, codegen_csharp_1.Try)((0, codegen_csharp_1.Return)(`${className}.FromJsonString(typeof(string) == sourceValue.GetType() ? sourceValue : sourceValue.ToJsonString());`));
                        yield (0, codegen_csharp_1.Catch)(undefined, '// Unable to use JSON pattern');
                        yield (0, codegen_csharp_1.If)((0, codegen_csharp_1.IsAssignableFrom)(powershell_declarations_1.PSObject, t), (0, codegen_csharp_1.Return)(`${className}.DeserializeFromPSObject(sourceValue)`));
                        yield (0, codegen_csharp_1.If)((0, codegen_csharp_1.IsAssignableFrom)(codegen_csharp_1.System.Collections.IDictionary, t), (0, codegen_csharp_1.Return)(`${className}.DeserializeFromDictionary(sourceValue)`));
                        // null if not successful
                        yield (0, codegen_csharp_1.Return)(codegen_csharp_1.dotnet.Null);
                    });
                }
            }
        }
    }
}
exports.ModelExtensionsNamespace = ModelExtensionsNamespace;
//# sourceMappingURL=model-extensions.js.map