"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.ValidationMethod = exports.CallMethod = exports.ResolveResponseType = exports.OperationMethod = exports.EventListener = void 0;
const codemodel_v3_1 = require("@azure-tools/codemodel-v3");
const codemodel_1 = require("@autorest/codemodel");
const linq_1 = require("@azure-tools/linq");
const codegen_1 = require("@azure-tools/codegen");
const codegen_csharp_1 = require("@azure-tools/codegen-csharp");
const binary_1 = require("../schema/binary");
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 codegen_csharp_9 = require("@azure-tools/codegen-csharp");
const codegen_csharp_10 = require("@azure-tools/codegen-csharp");
const codegen_csharp_11 = require("@azure-tools/codegen-csharp");
const codegen_csharp_12 = require("@azure-tools/codegen-csharp");
const clientruntime_1 = require("../clientruntime");
const parameter_1 = require("../operation/parameter");
const schema_1 = require("../../utils/schema");
const codemodel_v3_2 = require("@azure-tools/codemodel-v3");
const codegen_csharp_13 = require("@azure-tools/codegen-csharp");
const codegen_csharp_14 = require("@azure-tools/codegen-csharp");
function removeEncoding(pp, paramName, kmt) {
    const up = pp.typeDeclaration.serializeToNode(kmt, pp, paramName, clientruntime_1.ClientRuntime.SerializationMode.None).value;
    return pp.param.extensions && pp.param.extensions['x-ms-skip-url-encoding'] ? up.replace(/global::System.Uri.EscapeDataString|System.Uri.EscapeDataString/g, '') : up;
}
class EventListener {
    constructor(expression, emitSignals, withResult) {
        this.expression = expression;
        this.emitSignals = emitSignals;
        this.withResult = withResult;
    }
    *signalNoCheck(eventName, ...additionalParameters) {
        if (this.emitSignals) {
            const params = (0, linq_1.length)(additionalParameters) > 0 ? `, ${additionalParameters.joinWith(each => typeof each === 'string' ? each : each.value)}` : '';
            yield `await ${this.expression.value}.Signal(${eventName}${params});`;
        }
    }
    *syncSignalNoCheck(eventName, ...additionalParameters) {
        if (this.emitSignals) {
            const params = (0, linq_1.length)(additionalParameters) > 0 ? `, ${additionalParameters.joinWith(each => typeof each === 'string' ? each : each.value)}` : '';
            yield `${this.expression.value}.Signal(${eventName}${params}).Wait();`;
        }
    }
    *signal(eventName, ...additionalParameters) {
        if (this.emitSignals) {
            const params = (0, linq_1.length)(additionalParameters) > 0 ? `, ${additionalParameters.joinWith(each => typeof each === 'string' ? each : each.value)}` : '';
            yield `await ${this.expression.value}.Signal(${eventName}${params}); if( ${this.expression.value}.Token.IsCancellationRequested ) { return${this.withResult ? ' null' : ''}; }`;
        }
        else {
            yield `if( ${this.expression.value}.CancellationToken.IsCancellationRequested ) { throw ${codegen_csharp_13.System.OperationCanceledException.new()}; }`;
        }
    }
    *syncSignal(eventName, ...additionalParameters) {
        if (this.emitSignals) {
            const params = (0, linq_1.length)(additionalParameters) > 0 ? `, ${additionalParameters.joinWith(each => typeof each === 'string' ? each : each.value)}` : '';
            yield `${this.expression.value}.Signal(${eventName}${params}).Wait(); if( ${this.expression.value}.Token.IsCancellationRequested ) { return${this.withResult ? ' null' : ''}; }`;
        }
        else {
            yield `if( ${this.expression.value}.CancellationToken.IsCancellationRequested ) { throw ${codegen_csharp_13.System.OperationCanceledException.new()} }`;
        }
    }
}
exports.EventListener = EventListener;
class OperationMethod extends codegen_csharp_3.Method {
    constructor(parent, operation, viaIdentity, state, viaJson = false, withResult, objectInitializer) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
        super(viaJson
            ? `${(_a = operation.language.csharp) === null || _a === void 0 ? void 0 : _a.name}ViaJsonString${withResult ? 'WithResult' : ''}`
            : viaIdentity
                ? `${(_b = operation.language.csharp) === null || _b === void 0 ? void 0 : _b.name}ViaIdentity${withResult ? 'WithResult' : ''}`
                : `${(_c = operation.language.csharp) === null || _c === void 0 ? void 0 : _c.name}${withResult ? 'WithResult' : ''}` || '', withResult ? codegen_csharp_13.System.Threading.Tasks.Task(ResolveResponseType(undefined, operation, state)) : codegen_csharp_13.System.Threading.Tasks.Task());
        this.parent = parent;
        this.operation = operation;
        this.viaIdentity = viaIdentity;
        this.state = state;
        this.viaJson = viaJson;
        this.callbacks = new Array();
        this.apply(objectInitializer);
        this.async = codegen_csharp_1.Modifier.Async;
        this.returnsDescription = `A <see cref="${withResult ? codegen_csharp_13.System.Threading.Tasks.Task(ResolveResponseType(undefined, operation, state)) : codegen_csharp_13.System.Threading.Tasks.Task()}" /> that will be complete when handling of the response is completed.`;
        const $this = this;
        this.callName = `${(_d = operation.language.csharp) === null || _d === void 0 ? void 0 : _d.name}${withResult ? 'WithResult' : ''}_Call`;
        this.push((0, codegen_csharp_11.Using)('NoSynchronizationContext', ''));
        // add parameters
        this.methodParameters = [];
        const identity = new codegen_csharp_4.Parameter('viaIdentity', codegen_csharp_13.System.String);
        if (this.viaIdentity) {
            this.addParameter(identity);
        }
        let baseUrl = '';
        const paths = [];
        const headers = [];
        const queries = [];
        const others = [];
        for (let index = 0; index < (0, linq_1.length)(this.operation.parameters) && this.operation.parameters; index++) {
            const value = this.operation.parameters[index];
            if (value.language.default.name === '$host') {
                baseUrl = value.clientDefaultValue;
                continue;
            }
            const p = new parameter_1.OperationParameter(this, value, this.state.path('parameters', index));
            if ((_e = value.language.csharp) === null || _e === void 0 ? void 0 : _e.constantValue) {
                const constTd = state.project.modelsNamespace.NewResolveTypeDeclaration(value.schema, true, state);
                p.defaultInitializer = constTd.deserializeFromString(codemodel_v3_2.KnownMediaType.UriParameter, new codegen_csharp_2.StringExpression(`${value.language.csharp.constantValue}`), (0, codegen_csharp_2.toExpression)(constTd.defaultOfType));
            }
            // don't add path parameters  when we're in identity mode
            if (!this.viaIdentity || ((_f = value.protocol.http) === null || _f === void 0 ? void 0 : _f.in) !== codemodel_v3_1.ParameterLocation.Path) {
                switch ((_g = value.protocol.http) === null || _g === void 0 ? void 0 : _g.in) {
                    case codemodel_v3_1.ParameterLocation.Path:
                        paths.push(p);
                        break;
                    case codemodel_v3_1.ParameterLocation.Header:
                        headers.push(p);
                        break;
                    case codemodel_v3_1.ParameterLocation.Query:
                        queries.push(p);
                        break;
                    default:
                        others.push(p);
                        break;
                }
            }
            else {
                this.add(function* () {
                    yield '';
                });
            }
            this.methodParameters.push(p);
        }
        [...paths, ...headers, ...queries, ...others].forEach(p => this.addParameter(p));
        if (baseUrl === '') {
            // Some services will make the host as an input parameter
            baseUrl = this.operation.requests ? (_h = this.operation.requests[0].protocol.http) === null || _h === void 0 ? void 0 : _h.uri : '';
        }
        this.description = ((_j = this.operation.language.csharp) === null || _j === void 0 ? void 0 : _j.description) || '';
        // add body paramter if there should be one.
        if (this.operation.requests && this.operation.requests.length && this.operation.requests[0].parameters && this.operation.requests[0].parameters.length) {
            // this request does have a request body.
            const param = this.operation.requests[0].parameters.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0);
            if (param) {
                if (!viaJson) {
                    this.bodyParameter = new parameter_1.OperationBodyParameter(this, 'body', param.language.default.description, param.schema, (_k = param.required) !== null && _k !== void 0 ? _k : false, this.state, {
                        // TODO: temp solution. We need a class like NewKnowMediaType
                        mediaType: (0, codemodel_v3_2.knownMediaType)(codemodel_v3_2.KnownMediaType.Json),
                        contentType: codemodel_v3_2.KnownMediaType.Json
                    });
                    this.addParameter(this.bodyParameter);
                }
                else {
                    this.addParameter(new codegen_csharp_4.Parameter('jsonString', codegen_csharp_13.System.String, {
                        description: `Json string supplied to the ${(_l = operation.language.csharp) === null || _l === void 0 ? void 0 : _l.name} operation`,
                    }));
                }
            }
        }
        for (const response of [...(0, linq_1.values)(this.operation.responses), ...(0, linq_1.values)(this.operation.exceptions)]) {
            const responseType = response.binary ? new binary_1.Binary(new codemodel_1.BinarySchema(''), true) : (response.schema ? state.project.modelsNamespace.NewResolveTypeDeclaration((response.schema), true, state) : null);
            const headerType = response.language.default.headerSchema ? state.project.modelsNamespace.NewResolveTypeDeclaration(response.language.default.headerSchema, true, state) : null;
            const newCallbackParameter = new parameter_1.CallbackParameter(((_m = response.language.csharp) === null || _m === void 0 ? void 0 : _m.name) || '', responseType, headerType, this.state, { description: (_o = response.language.csharp) === null || _o === void 0 ? void 0 : _o.description });
            if (!withResult) {
                this.addParameter(newCallbackParameter);
            }
            this.callbacks.push(newCallbackParameter);
        }
        // add eventhandler parameter
        this.contextParameter = this.addParameter(new codegen_csharp_4.Parameter('eventListener', clientruntime_1.ClientRuntime.IEventListener, { description: `an <see cref="${clientruntime_1.ClientRuntime.IEventListener}" /> instance that will receive events.` }));
        // add optional parameter for sender
        this.senderParameter = this.addParameter(new codegen_csharp_4.Parameter('sender', clientruntime_1.ClientRuntime.ISendAsync, { description: `an instance of an ${clientruntime_1.ClientRuntime.ISendAsync} pipeline to use to make the request.` }));
        let rx = this.operation.requests ? (_p = this.operation.requests[0].protocol.http) === null || _p === void 0 ? void 0 : _p.path : '';
        const path = rx;
        let pathWithoutOperation = path;
        // For post API, Some URI may contain an action string .e.x '/start' at the end
        // of the URI, for such cases, we will drop the action string if identityCorrection
        // is set in the configuration
        if (this.operation.requests && this.operation.requests.length && ((_q = this.operation.requests[0].protocol.http) === null || _q === void 0 ? void 0 : _q.method) === 'post' && this.state.project.identityCorrection) {
            const idx = rx.lastIndexOf('/');
            rx = rx.substr(0, idx);
            pathWithoutOperation = rx;
        }
        let url = `/${path.startsWith('/') ? path.substr(1) : path}`;
        const serverParams = this.methodParameters.filter(each => { var _a; return ((_a = each.param.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === codemodel_v3_1.ParameterLocation.Uri; });
        const headerParams = this.methodParameters.filter(each => { var _a; return ((_a = each.param.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === codemodel_v3_1.ParameterLocation.Header; });
        const pathParams = this.methodParameters.filter(each => { var _a; return ((_a = each.param.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === codemodel_v3_1.ParameterLocation.Path; });
        const queryParams = this.methodParameters.filter(each => { var _a; return ((_a = each.param.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === codemodel_v3_1.ParameterLocation.Query; });
        const cookieParams = this.methodParameters.filter(each => { var _a; return ((_a = each.param.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === codemodel_v3_1.ParameterLocation.Cookie; });
        // replace any server params in the uri
        for (const pp of serverParams) {
            url = url.replace(`{${pp.param.language.default.serializedName}}`, `"
        + ${pp.name}
        + "`);
        }
        for (const pp of pathParams) {
            rx = rx.replace(`{${pp.param.language.default.serializedName}}`, `(?<${pp.param.language.default.serializedName}>[^/]+)`);
            if (this.viaIdentity) {
                url = url.replace(`{${pp.param.language.default.serializedName}}`, `"
        + ${pp.name}
        + "`);
            }
            else {
                url = url.replace(`{${pp.param.language.default.serializedName}}`, `"
        + ${removeEncoding(pp, '', codemodel_v3_2.KnownMediaType.UriParameter)}
        + "`);
            }
        }
        rx = `"^${rx}$"`;
        url = url.replace(/\s*\+ ""/gm, '');
        const bp = this.bodyParameter;
        if (bp) {
            this.serializationMode = clientruntime_1.ClientRuntime.SerializationMode.IncludeCreateOrUpdate;
            if (operation.language.default.name === 'Patch') {
                this.serializationMode = clientruntime_1.ClientRuntime.SerializationMode.IncludeUpdate;
            }
            // add optional parameter for json serialization mode
            this.serializationModeParameter = this.addParameter(new codegen_csharp_4.Parameter('serializationMode', clientruntime_1.ClientRuntime.SerializationMode, { description: `Allows the caller to choose the depth of the serialization. See <see cref="${clientruntime_1.ClientRuntime.SerializationMode}"/>.`, defaultInitializer: this.serializationMode }));
        }
        // add method implementation...
        this.add(function* () {
            var _a;
            const eventListener = new EventListener($this.contextParameter, $this.state.project.emitSignals, withResult);
            yield codegen_1.EOL;
            if ($this.viaIdentity) {
                yield '// verify that Identity format is an exact match for uri';
                yield codegen_1.EOL;
                const match = (0, codegen_csharp_12.Local)('_match', `${codegen_csharp_13.System.Text.RegularExpressions.Regex.new(rx, 'global::System.Text.RegularExpressions.RegexOptions.IgnoreCase').value}.Match(${identity.value})`);
                yield match.declarationStatement;
                yield (0, codegen_csharp_7.If)(`!${match}.Success`, `throw new global::System.Exception("Invalid identity for URI '${pathWithoutOperation}'");`);
                yield codegen_1.EOL;
                yield '// replace URI parameters with values from identity';
                for (const pp of pathParams) {
                    yield `var ${pp.name} = ${match.value}.Groups["${pp.param.language.default.serializedName}"].Value;`;
                }
            }
            yield '// construct URL';
            const pathAndQueryV = (0, codegen_csharp_12.Local)('pathAndQuery', `${codegen_csharp_13.System.Text.RegularExpressions.Regex.declaration}.Replace(
        "${url}"
        ${queryParams.length > 0 ? '+ "?"' : ''}${queryParams.joinWith(pp => `
        + ${removeEncoding(pp, pp.param.language.default.serializedName, codemodel_v3_2.KnownMediaType.QueryParameter)}`, `
        + "&"`)}
        ,"\\\\?&*$|&*$|(\\\\?)&+|(&)&+","$1$2")`.replace(/\s*\+ ""/gm, ''));
            yield pathAndQueryV.declarationStatement;
            yield codegen_1.EOL;
            yield eventListener.signal(clientruntime_1.ClientRuntime.Events.URLCreated, pathAndQueryV.value);
            yield codegen_1.EOL;
            yield '// generate request object ';
            const urlV = new codegen_csharp_12.LocalVariable('_url', codegen_csharp_13.dotnet.Var, {
                initializer: codegen_csharp_13.System.Uri.new(`$"${baseUrl}{${pathAndQueryV.value}}"`)
            });
            yield urlV.declarationStatement;
            const method = $this.operation.requests ? (_a = $this.operation.requests[0].protocol.http) === null || _a === void 0 ? void 0 : _a.method : '';
            yield `var request =  ${codegen_csharp_13.System.Net.Http.HttpRequestMessage.new(`${clientruntime_1.ClientRuntime.fullName}.Method.${method.capitalize()}, ${urlV.value}`)};`;
            yield eventListener.signal(clientruntime_1.ClientRuntime.Events.RequestCreated, 'request.RequestUri.PathAndQuery');
            yield codegen_1.EOL;
            if ((0, linq_1.length)(headerParams) > 0) {
                yield '// add headers parameters';
                for (const hp of headerParams) {
                    if (hp.param.language.default.name === 'Content-Length') {
                        // content length is set when the request body is set
                        continue;
                    }
                    yield hp.serializeToContainerMember(codemodel_v3_2.KnownMediaType.Header, new codegen_csharp_12.LocalVariable('request.Headers', codegen_csharp_13.dotnet.Var), hp.param.language.default.serializedName, clientruntime_1.ClientRuntime.SerializationMode.None);
                }
                yield codegen_1.EOL;
            }
            yield eventListener.signal(clientruntime_1.ClientRuntime.Events.HeaderParametersAdded);
            if (bp) {
                yield '// set body content';
                yield `request.Content = ${bp.serializeToContent(bp.mediaType, new codegen_csharp_2.LiteralExpression('serializationMode'))};`;
                yield `request.Content.Headers.ContentType = ${codegen_csharp_13.System.Net.Http.Headers.MediaTypeHeaderValue.Parse(bp.contentType)};`;
                yield eventListener.signal(clientruntime_1.ClientRuntime.Events.BodyContentSet);
            }
            if (viaJson) {
                yield '// set body content';
                yield 'request.Content = new global::System.Net.Http.StringContent(jsonString, global::System.Text.Encoding.UTF8);';
                yield 'request.Content.Headers.ContentType = global::System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");';
                yield eventListener.signal(clientruntime_1.ClientRuntime.Events.BodyContentSet);
            }
            yield '// make the call ';
        });
    }
    emitCall(returnFromCall, withResult) {
        // storage will return from the call for download, etc.
        if (returnFromCall) {
            this.returnType = codegen_csharp_13.System.Threading.Tasks.Task(codegen_csharp_13.System.Net.Http.HttpResponseMessage);
        }
        this.add(`${withResult ? 'return ' : ''}await this.${this.callName} (request, ${withResult ? '' : `${this.callbacks.joinWith(each => each.use, ',')},`}${this.contextParameter.use},${this.senderParameter.use}); `);
        // remove constant parameters and make them locals instead.
        this.insert('// Constant Parameters');
        for (let i = (0, linq_1.length)(this.parameters); i--; i < 0) {
            const p = this.parameters[i];
            if (p && p.defaultInitializer && p.name !== 'serializationMode') {
                this.parameters.splice(i, 1);
                this.insert(new codegen_csharp_12.LocalVariable(p.name, codegen_csharp_13.dotnet.Var, { initializer: p.defaultInitializer }));
            }
        }
    }
}
exports.OperationMethod = OperationMethod;
function ResolveResponseType(opMethod, operation, state) {
    let typeCount = 0;
    let responseType = undefined;
    if (opMethod) {
        opMethod.callbacks.filter(each => each.name !== 'onDefault').forEach(each => {
            if (each.responseType && responseType && each.responseType !== responseType) {
                typeCount++;
            }
            else if (each.responseType && !responseType) {
                responseType = each.responseType;
                typeCount = 1;
            }
        });
    }
    else if (operation && state) {
        for (const response of [...(0, linq_1.values)(operation.responses), ...(0, linq_1.values)(operation.exceptions)].filter(each => { var _a, _b; return ((_b = (_a = each.language) === null || _a === void 0 ? void 0 : _a.csharp) === null || _b === void 0 ? void 0 : _b.name) !== 'onDefault'; })) {
            const eachResponseType = response.binary ? new binary_1.Binary(new codemodel_1.BinarySchema(''), true) : (response.schema ? state.project.modelsNamespace.NewResolveTypeDeclaration((response.schema), true, state) : undefined);
            if (eachResponseType && responseType && eachResponseType !== responseType) {
                typeCount++;
            }
            else if (eachResponseType && !responseType) {
                responseType = eachResponseType;
                typeCount = 1;
            }
        }
    }
    return typeCount === 1 ? responseType : undefined;
}
exports.ResolveResponseType = ResolveResponseType;
class CallMethod extends codegen_csharp_3.Method {
    constructor(parent, opMethod, state, objectInitializer, withResult) {
        super(`${opMethod.name}_Call`, withResult && ResolveResponseType(opMethod) ? codegen_csharp_13.System.Threading.Tasks.Task(ResolveResponseType(opMethod)) : codegen_csharp_13.System.Threading.Tasks.Task());
        this.parent = parent;
        this.opMethod = opMethod;
        this.state = state;
        this.returnNull = false;
        this.description = `Actual wire call for <see cref= "${opMethod.name}" /> method.`;
        this.returnsDescription = opMethod.returnsDescription;
        this.apply(objectInitializer);
        this.access = codegen_csharp_1.Access.Internal;
        this.async = codegen_csharp_1.Modifier.Async;
        this.push((0, codegen_csharp_11.Using)('NoSynchronizationContext', ''));
        const $this = this;
        // add parameters
        // request, listener, sender
        const reqParameter = this.addParameter(new codegen_csharp_4.Parameter('request', codegen_csharp_13.System.Net.Http.HttpRequestMessage, { description: 'the prepared HttpRequestMessage to send.' }));
        if (!withResult) {
            opMethod.callbacks.forEach(each => this.addParameter(each));
        }
        this.addParameter(opMethod.contextParameter);
        this.addParameter(opMethod.senderParameter);
        // add statements to this method
        this.add(function* () {
            const eventListener = new EventListener(opMethod.contextParameter, $this.state.project.emitSignals, withResult);
            const response = (0, codegen_csharp_12.Local)('_response', codegen_csharp_13.dotnet.Null, codegen_csharp_13.System.Net.Http.HttpResponseMessage);
            yield response;
            yield (0, codegen_csharp_10.Try)(function* () {
                var _a, _b, _c, _d, _e, _f;
                const responder = function* () {
                    // TODO: omit generating _contentType var if it will never be used
                    // const contentType = new LocalVariable('_contentType', dotnet.Var, { initializer: `_response.Content.Headers.ContentType?.MediaType` });
                    const contentType = (0, codegen_csharp_12.Local)('_contentType', `${response}.Content.Headers.ContentType?.MediaType`);
                    yield contentType;
                    // add response handlers
                    yield (0, codegen_csharp_9.Switch)(`${response}.StatusCode`, function* () {
                        var _a;
                        const responses = [...(0, linq_1.values)(opMethod.operation.responses), ...(0, linq_1.values)(opMethod.operation.exceptions)].sort(function (a, b) { var _a, _b; return ((_a = a.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0]).localeCompare(((_b = b.protocol.http) === null || _b === void 0 ? void 0 : _b.statusCodes[0])); });
                        for (const resp of responses) {
                            const responseCode = (_a = resp.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0];
                            if (responseCode !== 'default' /*TODO: !== not found, handle other exception response */) {
                                const leadNum = parseInt(responseCode[0]);
                                // will use enum when it can, fall back to casting int when it can't
                                if (withResult) {
                                    yield (0, codegen_csharp_5.TerminalCase)(codegen_csharp_13.System.Net.HttpStatusCode[responseCode] ? codegen_csharp_13.System.Net.HttpStatusCode[responseCode].value : `${codegen_csharp_13.System.Net.HttpStatusCode.declaration} n when((int)n >= ${leadNum * 100} && (int)n < ${leadNum * 100 + 100})`, $this.responsesEmitter($this, opMethod, [resp], response, eventListener, withResult));
                                }
                                else {
                                    yield (0, codegen_csharp_5.Case)(codegen_csharp_13.System.Net.HttpStatusCode[responseCode] ? codegen_csharp_13.System.Net.HttpStatusCode[responseCode].value : `${codegen_csharp_13.System.Net.HttpStatusCode.declaration} n when((int)n >= ${leadNum * 100} && (int)n < ${leadNum * 100 + 100})`, $this.responsesEmitter($this, opMethod, [resp], response, eventListener, false));
                                }
                            }
                            else {
                                if (withResult) {
                                    yield (0, codegen_csharp_5.TerminalDefaultCase)($this.responsesEmitter($this, opMethod, [resp], response, eventListener, withResult));
                                }
                                else {
                                    yield (0, codegen_csharp_5.DefaultCase)($this.responsesEmitter($this, opMethod, [resp], response, eventListener, false));
                                }
                            }
                        }
                        // missing default response?
                        if (!opMethod.operation.exceptions) {
                            // if no default, we need one that handles the rest of the stuff.
                            yield (0, codegen_csharp_5.TerminalDefaultCase)(function* () {
                                yield `throw new ${clientruntime_1.ClientRuntime.fullName}.UndeclaredResponseException(_response);`;
                            });
                        }
                    });
                };
                const originalUri = (0, codegen_csharp_12.Local)('_originalUri', new codegen_csharp_2.LiteralExpression(`${reqParameter.use}.RequestUri.AbsoluteUri`));
                if ((_a = $this.opMethod.operation.language.csharp) === null || _a === void 0 ? void 0 : _a.lro) {
                    yield '// this operation supports x-ms-long-running-operation';
                    yield originalUri;
                }
                // try statements
                const sendTask = (0, codegen_csharp_12.Local)('sendTask', new codegen_csharp_2.LiteralExpression(`${opMethod.senderParameter.value}.SendAsync(${reqParameter.use}, ${opMethod.contextParameter.value})`));
                yield sendTask;
                // delay sending BeforeCall event until URI has been replaced by HTTP pipeline
                yield eventListener.signal(clientruntime_1.ClientRuntime.Events.BeforeCall, reqParameter.use);
                if ((_b = $this.opMethod.operation.language.csharp) === null || _b === void 0 ? void 0 : _b.lro) {
                    yield eventListener.signal(clientruntime_1.ClientRuntime.Events.Progress, new codegen_csharp_2.LiteralExpression('"intentional placeholder"'), new codegen_csharp_2.LiteralExpression('0'));
                }
                yield `${response.value} = await ${sendTask.value}; `;
                yield eventListener.signal(clientruntime_1.ClientRuntime.Events.ResponseCreated, response.value);
                const EOL = 'EOL';
                // LRO processing (if appropriate)
                if ((_c = $this.opMethod.operation.language.csharp) === null || _c === void 0 ? void 0 : _c.lro) {
                    yield `// declared final-state-via: ${$this.opMethod.operation.language.csharp.lro['final-state-via']}`;
                    const fsv = $this.opMethod.operation.language.csharp.lro['final-state-via'];
                    let finalUri;
                    switch (fsv) {
                        case 'original-uri':
                            // perform a final GET on the original URI.
                            finalUri = originalUri;
                            break;
                        case 'location':
                            // perform a final GET on the uri in Location header
                            finalUri = (0, codegen_csharp_12.Local)('_finalUri', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Location')));
                            yield finalUri;
                            break;
                        case 'operation-location':
                            // perform a final GET on the uri in Operation-Location header
                            finalUri = (0, codegen_csharp_12.Local)('_finalUri', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Operation-Location')));
                            yield finalUri;
                            break;
                        case 'azure-asyncoperation':
                        case 'azure-async-operation':
                            //depending on the type of request, do the appropriate behavior
                            switch ((_e = (_d = $this.opMethod.operation.requests) === null || _d === void 0 ? void 0 : _d[0].protocol.http) === null || _e === void 0 ? void 0 : _e.method.toLowerCase()) {
                                case 'post':
                                case 'delete':
                                    finalUri = (0, codegen_csharp_12.Local)('_finalUri', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Azure-AsyncOperation')));
                                    yield finalUri;
                                    break;
                                case 'patch':
                                case 'put':
                                    // perform a final GET on the original URI.
                                    finalUri = originalUri;
                                    break;
                            }
                            break;
                        default:
                            // depending on the type of request, fall back to the appropriate behavior
                            if ($this.opMethod.operation.requests) {
                                switch ((_f = $this.opMethod.operation.requests[0].protocol.http) === null || _f === void 0 ? void 0 : _f.method.toLowerCase()) {
                                    case 'post':
                                    case 'delete':
                                        finalUri = (0, codegen_csharp_12.Local)('_finalUri', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Location')));
                                        yield finalUri;
                                        break;
                                    case 'patch':
                                    case 'put':
                                        // perform a final GET on the original URI.
                                        finalUri = originalUri;
                                        break;
                                }
                            }
                            break;
                    }
                    const asyncOperation = (0, codegen_csharp_12.Local)('asyncOperation', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Azure-AsyncOperation')));
                    yield asyncOperation;
                    const location = (0, codegen_csharp_12.Local)('location', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Location')));
                    yield location;
                    const operationLocation = (0, codegen_csharp_12.Local)('operationLocation', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Operation-Location')));
                    yield operationLocation;
                    yield (0, codegen_csharp_7.While)(new codegen_csharp_2.LiteralExpression(`${reqParameter.use}.Method == System.Net.Http.HttpMethod.Put && ${response.value}.StatusCode == ${codegen_csharp_13.System.Net.HttpStatusCode[200].value} || ${response.value}.StatusCode == ${codegen_csharp_13.System.Net.HttpStatusCode[201].value} || ${response.value}.StatusCode == ${codegen_csharp_13.System.Net.HttpStatusCode[202].value} `), function* () {
                        yield '// delay before making the next polling request';
                        yield eventListener.signal(clientruntime_1.ClientRuntime.Events.DelayBeforePolling, response.value);
                        yield EOL;
                        yield '// while we wait, let\'s grab the headers and get ready to poll. ';
                        yield 'if (!System.String.IsNullOrEmpty(_response.GetFirstHeader(@"Azure-AsyncOperation"))) {';
                        yield '    ' + asyncOperation.assign(response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Azure-AsyncOperation')));
                        yield '}';
                        yield 'if (!global::System.String.IsNullOrEmpty(_response.GetFirstHeader(@"Location"))) {';
                        yield '    ' + location.assign(response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Location')));
                        yield '}';
                        yield 'if (!global::System.String.IsNullOrEmpty(_response.GetFirstHeader(@"Operation-Location"))) {';
                        yield '    ' + operationLocation.assign(response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Operation-Location')));
                        yield '}';
                        const uriLocal = (0, codegen_csharp_12.Local)('_uri', (0, codegen_csharp_14.Ternery)(codegen_csharp_13.System.String.IsNullOrEmpty(asyncOperation), (0, codegen_csharp_14.Ternery)(codegen_csharp_13.System.String.IsNullOrEmpty(location), (0, codegen_csharp_14.Ternery)(codegen_csharp_13.System.String.IsNullOrEmpty(operationLocation), originalUri, operationLocation), location), asyncOperation));
                        yield uriLocal;
                        yield `${reqParameter.use} = ${reqParameter.use}.CloneAndDispose(${codegen_csharp_13.System.Uri.new(uriLocal)}, ${clientruntime_1.ClientRuntime.Method.Get});`;
                        yield EOL;
                        yield '// and let\'s look at the current response body and see if we have some information we can give back to the listener';
                        const content = (0, codegen_csharp_12.Local)('content', new codegen_csharp_2.LiteralExpression(`await ${response.value}.Content.ReadAsStringAsync()`));
                        yield content;
                        yield EOL;
                        yield '// drop the old response';
                        yield `${response.value}?.Dispose();`;
                        yield EOL;
                        yield '// make the polling call';
                        yield `${response.value} = await ${opMethod.senderParameter}.SendAsync(${reqParameter.value}, ${opMethod.contextParameter});`;
                        yield eventListener.signal(clientruntime_1.ClientRuntime.Events.Polling, response.value);
                        yield EOL;
                        yield `
  // if we got back an OK, take a peek inside and see if it's done
  if( ${response.value}.StatusCode == ${codegen_csharp_13.System.Net.HttpStatusCode.OK})
  {
      var error = false;
      try {
          if( ${clientruntime_1.ClientRuntime.JsonNode.Parse((0, codegen_csharp_2.toExpression)(`await ${response.value}.Content.ReadAsStringAsync()`))} is ${clientruntime_1.ClientRuntime.JsonObject} json)
          {
              var state = json.Property("properties")?.PropertyT<${clientruntime_1.ClientRuntime.JsonString}>("provisioningState") ?? json.PropertyT<${clientruntime_1.ClientRuntime.JsonString}>("status");
              if( state is null )
              {
                  // the body doesn't contain any information that has the state of the LRO
                  // we're going to just get out, and let the consumer have the result
                  break;
              }

              switch( state?.ToString()?.ToLower() )
              {
                case "failed":
                    error = true;
                    break;
                case "succeeded":
                case "canceled":
                  // we're done polling.
                  break;

                default:
                  // need to keep polling!
                  ${response.value}.StatusCode = ${codegen_csharp_13.System.Net.HttpStatusCode.Created};
                  continue;
              }
          }
      } catch {
          // if we run into a problem peeking into the result,
          // we really don't want to do anything special.
      }
      if (error) {
          throw new ${clientruntime_1.ClientRuntime.fullName}.UndeclaredResponseException(${response.value});
      }
  }`;
                        yield EOL;
                        yield '// check for terminal status code';
                        yield (0, codegen_csharp_7.If)(new codegen_csharp_2.LiteralExpression(`${response.value}.StatusCode == ${codegen_csharp_13.System.Net.HttpStatusCode[201].value} || ${response.value}.StatusCode == ${codegen_csharp_13.System.Net.HttpStatusCode[202].value} `), 'continue;');
                        yield '// we are done polling, do a request on final target?';
                        switch (fsv) {
                            case 'original-uri':
                            case 'azure-asyncoperation':
                            case 'azure-async-operation':
                            case 'location':
                            case 'operation-location':
                                // perform a final GET on the specified final URI.
                                yield $this.finalGet(eventListener, finalUri, reqParameter, response);
                                break;
                            default:
                                yield (0, codegen_csharp_7.If)(`!string.IsNullOrWhiteSpace(${finalUri})`, function* () {
                                    yield $this.finalGet(eventListener, finalUri, reqParameter, response);
                                });
                                break;
                        }
                    });
                }
                yield eventListener.signal(clientruntime_1.ClientRuntime.Events.Progress, new codegen_csharp_2.LiteralExpression('"intentional placeholder"'), new codegen_csharp_2.LiteralExpression('100'));
                yield responder();
            });
            yield (0, codegen_csharp_6.Finally)(function* () {
                yield '// finally statements';
                yield eventListener.signalNoCheck(clientruntime_1.ClientRuntime.Events.Finally, 'request', '_response');
                yield `${response.value}?.Dispose();`;
                yield `${reqParameter.use}?.Dispose();`;
            });
            if ($this.returnNull) {
                yield (0, codegen_csharp_8.Return)('result');
                $this.insert(new codegen_csharp_12.LocalVariable('result', codegen_csharp_13.System.Net.Http.HttpResponseMessage, { initializer: codegen_csharp_13.dotnet.Null }));
            }
        });
        this.opMethod.emitCall($this.returnNull, withResult);
    }
    *finalGet(eventListener, finalLocation, reqParameter, response) {
        yield '// create a new request with the final uri';
        yield reqParameter.assign(`${(0, codegen_csharp_2.valueOf)(reqParameter)}.CloneAndDispose(${codegen_csharp_13.System.Uri.new(finalLocation)}, ${clientruntime_1.ClientRuntime.Method.Get})`);
        yield codegen_1.EOL;
        yield '// drop the old response';
        yield `${response.value}?.Dispose();`;
        yield codegen_1.EOL;
        yield '// make the final call';
        yield response.assign(`await ${this.opMethod.senderParameter}.SendAsync(${(0, codegen_csharp_2.valueOf)(reqParameter)},  ${this.opMethod.contextParameter})`);
        yield eventListener.signal(clientruntime_1.ClientRuntime.Events.Polling, response.value);
        // make sure we're not polling anymore.
        yield 'break;';
    }
    *responsesEmitter($this, opMethod, responses, responseVariable, eventListener, withResult) {
        var _a;
        if ((0, linq_1.length)(responses) > 1) {
            yield (0, codegen_csharp_9.Switch)('_contentType', function* () {
                var _a, _b, _c, _d;
                for (const eachResponse of (0, linq_1.values)(responses)) {
                    const mimetype = (0, linq_1.length)((_a = eachResponse.protocol.http) === null || _a === void 0 ? void 0 : _a.mediaTypes) > 0 ? (_b = eachResponse.protocol.http) === null || _b === void 0 ? void 0 : _b.mimeTypes[0] : '';
                    const callbackParameter = (0, linq_1.values)(opMethod.callbacks).first(each => { var _a; return each.name === ((_a = eachResponse.language.csharp) === null || _a === void 0 ? void 0 : _a.name); });
                    let count = (0, linq_1.length)((_c = eachResponse.protocol.http) === null || _c === void 0 ? void 0 : _c.mediaTypes);
                    for (const mt of (0, linq_1.values)((_d = eachResponse.protocol.http) === null || _d === void 0 ? void 0 : _d.mediaTypes)) {
                        count--;
                        const mediaType = (0, codemodel_v3_2.normalizeMediaType)(mt);
                        if (mediaType) {
                            if (count === 0) {
                                yield (0, codegen_csharp_5.Case)(new codegen_csharp_2.StringExpression(mediaType).toString(), $this.NewResponseHandler(mimetype, eachResponse, callbackParameter, responseVariable, withResult));
                            }
                            else {
                                yield (0, codegen_csharp_5.TerminalCase)(new codegen_csharp_2.StringExpression(mediaType).toString(), '');
                            }
                        }
                    }
                }
            });
        }
        else {
            const response = responses[0];
            const callbackParameter = (0, linq_1.values)(opMethod.callbacks).first(each => { var _a; return each.name === ((_a = response.language.csharp) === null || _a === void 0 ? void 0 : _a.name); });
            // all mimeTypes per for this response code.
            yield eventListener.signal(clientruntime_1.ClientRuntime.Events.BeforeResponseDispatch, responseVariable.value);
            yield $this.NewResponseHandler((0, linq_1.values)((_a = response.protocol.http) === null || _a === void 0 ? void 0 : _a.mediaTypes).first() || '', response, callbackParameter, responseVariable, withResult);
        }
    }
    *responseHandlerForNormalPipeline(mimetype, eachResponse, callbackParameter) {
        const callbackParameters = new Array();
        if (callbackParameter.responseType) {
            // hande the body response
            const r = callbackParameter.responseType.deserializeFromResponse((0, codemodel_v3_2.knownMediaType)(mimetype), (0, codegen_csharp_2.toExpression)('_response'), (0, codegen_csharp_2.toExpression)('null'));
            if (r) {
                callbackParameters.push(r);
            }
            // if (parseMediaType(mimetype)) {
            // this media type isn't directly supported by deserialization
            // we can return a stream to the consumer instead
            // }
        }
        if (callbackParameter.headerType) {
            // header model deserialization...
            const r = callbackParameter.headerType.deserializeFromResponse(codemodel_v3_2.KnownMediaType.Header, (0, codegen_csharp_2.toExpression)('_response'), (0, codegen_csharp_2.toExpression)('null'));
            if (r) {
                callbackParameters.push(r);
            }
        }
        // make the callback with the appropriate parameters
        yield `await ${eachResponse.details.csharp.name}(_response${callbackParameters.length === 0 ? '' : ','}${callbackParameters.joinWith(codegen_csharp_2.valueOf)});`;
    }
    *NewResponseHandlerForNormalPipeline(mimetype, eachResponse, callbackParameter, responseVariable) {
        var _a;
        const callbackParameters = new Array();
        if (callbackParameter.responseType) {
            // hande the body response
            const r = callbackParameter.responseType.deserializeFromResponse((0, codemodel_v3_2.knownMediaType)(mimetype), (0, codegen_csharp_2.toExpression)(responseVariable.value), (0, codegen_csharp_2.toExpression)('null'));
            if (r) {
                callbackParameters.push(r);
            }
            // if (parseMediaType(mimetype)) {
            // this media type isn't directly supported by deserialization
            // we can return a stream to the consumer instead
            // }
        }
        if (callbackParameter.headerType) {
            // header model deserialization...
            const r = callbackParameter.headerType.deserializeFromResponse(codemodel_v3_2.KnownMediaType.Header, (0, codegen_csharp_2.toExpression)(responseVariable.value), (0, codegen_csharp_2.toExpression)('null'));
            if (r) {
                callbackParameters.push(r);
            }
        }
        // make the callback with the appropriate parameters
        yield `await ${(_a = eachResponse.language.csharp) === null || _a === void 0 ? void 0 : _a.name}(_response${callbackParameters.length === 0 ? '' : ','}${callbackParameters.joinWith(codegen_csharp_2.valueOf)});`;
    }
    *NewResponseHandlerWithResult(mimetype, response, callbackParameter, responseVariable) {
        var _a, _b, _c, _d;
        const responseType = callbackParameter.responseType;
        if (!responseType) {
            yield `throw new ${clientruntime_1.ClientRuntime.fullName}.UndeclaredResponseException(${responseVariable.value});`;
            return;
        }
        const deserializeResponseAsync = responseType.deserializeFromResponse((0, codemodel_v3_2.knownMediaType)(mimetype), (0, codegen_csharp_2.toExpression)(responseVariable.value), (0, codegen_csharp_2.toExpression)('null'));
        yield `var _result = ${deserializeResponseAsync};`;
        switch ((_a = response.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0]) {
            case 'default': {
                // this should write an error to the error channel.
                yield `// Error Response : ${(_b = response.protocol.http) === null || _b === void 0 ? void 0 : _b.statusCodes[0]}`;
                const unexpected = function* () {
                    yield '// Unrecognized Response. Create an error record based on what we have.';
                    const ex = responseType ?
                        (0, codegen_csharp_12.Local)('ex', `new ${clientruntime_1.ClientRuntime.name}.RestException<${responseType}>(${responseVariable.value}, await _result)`) :
                        (0, codegen_csharp_12.Local)('ex', `new ${clientruntime_1.ClientRuntime.name}.RestException(responseMessage)`);
                    yield ex.declarationStatement;
                    yield `throw ${ex};`;
                };
                if (response.schema !== undefined) {
                    // the schema should be the error information.
                    // this supports both { error { message, code} } and { message, code}
                    let props = (0, schema_1.getAllPublicVirtualProperties)((_c = response.schema.language.csharp) === null || _c === void 0 ? void 0 : _c.virtualProperties);
                    const errorProperty = (0, linq_1.values)(props).first(p => p.property.serializedName === 'error');
                    let ep = '';
                    if (errorProperty) {
                        props = (0, schema_1.getAllPublicVirtualProperties)((_d = errorProperty.property.schema.language.csharp) === null || _d === void 0 ? void 0 : _d.virtualProperties);
                        ep = `${errorProperty.name}?.`;
                    }
                    const codeProp = props.find(p => p.name.toLowerCase().indexOf('code') > -1); // first property with 'code'
                    const messageProp = props.find(p => p.name.toLowerCase().indexOf('message') > -1); // first property with 'message'
                    const actionProp = props.find(p => p.name.toLowerCase().indexOf('action') > -1); // first property with 'action'
                    if (codeProp && messageProp) {
                        const lcode = new codegen_csharp_12.LocalVariable('code', codegen_csharp_13.dotnet.Var, { initializer: `(await _result)?.${ep}${codeProp.name}` });
                        const lmessage = new codegen_csharp_12.LocalVariable('message', codegen_csharp_13.dotnet.Var, { initializer: `(await _result)?.${ep}${messageProp.name}` });
                        const laction = actionProp ? new codegen_csharp_12.LocalVariable('action', codegen_csharp_13.dotnet.Var, { initializer: `(await _result)?.${ep}${actionProp.name} ?? ${codegen_csharp_13.System.String.Empty}` }) : undefined;
                        yield lcode;
                        yield lmessage;
                        yield laction;
                        yield (0, codegen_csharp_7.If)((0, codegen_csharp_1.Or)((0, codegen_csharp_1.IsNull)(lcode), ((0, codegen_csharp_1.IsNull)(lmessage))), unexpected);
                        yield (0, codegen_csharp_1.Else)(`throw new global::System.Exception($"[{${lcode}}] : {${lmessage}}");`);
                    }
                    else {
                        yield unexpected;
                    }
                }
                else {
                    yield unexpected;
                }
                break;
            }
            default: {
                yield 'return await _result;';
                break;
            }
        }
    }
    responseHandler(mimetype, eachResponse, callbackParameter) {
        return this.responseHandlerForNormalPipeline(mimetype, eachResponse, callbackParameter);
    }
    NewResponseHandler(mimetype, eachResponse, callbackParameter, responseVariable, withResult) {
        return withResult ? this.NewResponseHandlerWithResult(mimetype, eachResponse, callbackParameter, responseVariable) : this.NewResponseHandlerForNormalPipeline(mimetype, eachResponse, callbackParameter, responseVariable);
    }
}
exports.CallMethod = CallMethod;
class ValidationMethod extends codegen_csharp_3.Method {
    constructor(parent, opMethod, state, objectInitializer) {
        super(`${opMethod.name}_Validate`, codegen_csharp_13.System.Threading.Tasks.Task());
        this.parent = parent;
        this.opMethod = opMethod;
        this.state = state;
        this.apply(objectInitializer);
        this.description = `Validation method for <see cref="${opMethod.name}" /> method. Call this like the actual call, but you will get validation events back.`;
        this.returnsDescription = opMethod.returnsDescription;
        this.access = codegen_csharp_1.Access.Internal;
        this.async = codegen_csharp_1.Modifier.Async;
        this.push((0, codegen_csharp_11.Using)('NoSynchronizationContext', ''));
        // add the method parameters
        for (const parameter of opMethod.methodParameters) {
            if (!parameter.defaultInitializer) {
                this.addParameter(parameter);
            }
        }
        if (opMethod.bodyParameter) {
            this.addParameter(opMethod.bodyParameter);
        }
        this.addParameter(opMethod.contextParameter);
        // add statements to this method
        this.add(function* () {
            for (const parameter of opMethod.methodParameters) {
                if (!parameter.defaultInitializer) {
                    // spit out parameter validation
                    yield parameter.validatePresenceStatement(opMethod.contextParameter);
                    yield parameter.validationStatement(opMethod.contextParameter);
                }
            }
            // spit out body parameter validation too
            if (opMethod.bodyParameter) {
                yield opMethod.bodyParameter.validatePresenceStatement(opMethod.contextParameter);
                yield opMethod.bodyParameter.validationStatement(opMethod.contextParameter);
            }
        });
    }
}
exports.ValidationMethod = ValidationMethod;
//# sourceMappingURL=method.js.map