"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 });
const codemodel_v3_1 = require("@azure-tools/codemodel-v3");
const codemodel_1 = require("@azure-tools/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 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) {
        this.expression = expression;
        this.emitSignals = emitSignals;
    }
    *signalNoCheck(eventName, ...additionalParameters) {
        if (this.emitSignals) {
            const params = 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 = 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 = 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; }`;
        }
        else {
            yield `if( ${this.expression.value}.CancellationToken.IsCancellationRequested ) { throw ${codegen_csharp_13.System.OperationCanceledException.new()}; }`;
        }
    }
    *syncSignal(eventName, ...additionalParameters) {
        if (this.emitSignals) {
            const params = 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; }`;
        }
        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, objectInitializer) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
        super(viaIdentity ? `${(_a = operation.language.csharp) === null || _a === void 0 ? void 0 : _a.name}ViaIdentity` : ((_b = operation.language.csharp) === null || _b === void 0 ? void 0 : _b.name) || '', codegen_csharp_13.System.Threading.Tasks.Task());
        this.parent = parent;
        this.operation = operation;
        this.viaIdentity = viaIdentity;
        this.state = state;
        this.callbacks = new Array();
        this.apply(objectInitializer);
        this.async = codegen_csharp_1.Modifier.Async;
        this.returnsDescription = `A <see cref="${codegen_csharp_13.System.Threading.Tasks.Task()}" /> that will be complete when handling of the response is completed.`;
        const $this = this;
        this.callName = `${(_c = operation.language.csharp) === null || _c === void 0 ? void 0 : _c.name}_Call`;
        this.push(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 = '';
        for (let index = 0; index < 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 ((_d = value.language.csharp) === null || _d === void 0 ? void 0 : _d.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}`), codegen_csharp_2.toExpression(constTd.defaultOfType));
            }
            // don't add path parameters  when we're in identity mode
            if (!this.viaIdentity || ((_e = value.protocol.http) === null || _e === void 0 ? void 0 : _e.in) !== codemodel_v3_1.ParameterLocation.Path) {
                this.addParameter(p);
            }
            else {
                this.add(function* () {
                    yield '';
                });
            }
            this.methodParameters.push(p);
        }
        if (baseUrl === '') {
            // Some services will make the host as an input parameter
            baseUrl = this.operation.requests ? (_f = this.operation.requests[0].protocol.http) === null || _f === void 0 ? void 0 : _f.uri : '';
        }
        this.description = ((_g = this.operation.language.csharp) === null || _g === void 0 ? void 0 : _g.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) {
                this.bodyParameter = new parameter_1.OperationBodyParameter(this, 'body', param.language.default.description, param.schema, (_h = param.required, (_h !== null && _h !== void 0 ? _h : false)), this.state, {
                    // TODO: temp solution. We need a class like NewKnowMediaType
                    mediaType: codemodel_v3_2.knownMediaType(codemodel_v3_2.KnownMediaType.Json),
                    contentType: codemodel_v3_2.KnownMediaType.Json
                });
                this.addParameter(this.bodyParameter);
            }
        }
        for (const response of [...linq_1.values(this.operation.responses), ...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(((_j = response.language.csharp) === null || _j === void 0 ? void 0 : _j.name) || '', responseType, headerType, this.state, { description: (_k = response.language.csharp) === null || _k === void 0 ? void 0 : _k.description });
            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 ? (_l = this.operation.requests[0].protocol.http) === null || _l === void 0 ? void 0 : _l.path : '';
        const path = rx;
        // 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 && ((_m = this.operation.requests[0].protocol.http) === null || _m === void 0 ? void 0 : _m.method) === 'post' && this.state.project.identityCorrection) {
            const idx = rx.lastIndexOf('/');
            rx = rx.substr(0, idx);
        }
        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;
        // add method implementation...
        this.add(function* () {
            var _a;
            const eventListener = new EventListener($this.contextParameter, $this.state.project.emitSignals);
            yield codegen_1.EOL;
            if ($this.viaIdentity) {
                yield '// verify that Identity format is an exact match for uri';
                yield codegen_1.EOL;
                const match = 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 codegen_csharp_7.If(`!${match}.Success`, `throw new global::System.Exception("Invalid identity for URI '${path}'");`);
                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 = 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 (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, clientruntime_1.ClientRuntime.SerializationMode.None)};`;
                yield `request.Content.Headers.ContentType = ${codegen_csharp_13.System.Net.Http.Headers.MediaTypeHeaderValue.Parse(bp.contentType)};`;
                yield eventListener.signal(clientruntime_1.ClientRuntime.Events.BodyContentSet);
            }
            yield '// make the call ';
        });
    }
    emitCall(returnFromCall) {
        // 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(`await this.${this.callName}(request,${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 = linq_1.length(this.parameters); i--; i < 0) {
            const p = this.parameters[i];
            if (p && p.defaultInitializer) {
                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;
class CallMethod extends codegen_csharp_3.Method {
    constructor(parent, opMethod, state, objectInitializer) {
        super(`${opMethod.name}_Call`, 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(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.' }));
        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);
            const response = codegen_csharp_12.Local('_response', codegen_csharp_13.dotnet.Null, codegen_csharp_13.System.Net.Http.HttpResponseMessage);
            yield response;
            yield codegen_csharp_10.Try(function* () {
                var _a, _b, _c, _d;
                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 = codegen_csharp_12.Local('_contentType', `${response}.Content.Headers.ContentType?.MediaType`);
                    yield contentType;
                    // add response handlers
                    yield codegen_csharp_9.Switch(`${response}.StatusCode`, function* () {
                        var _a, _b;
                        var responses = [...linq_1.values(opMethod.operation.responses), ...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) {
                            if (((_a = resp.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0]) !== 'default') {
                                const responseCode = (_b = resp.protocol.http) === null || _b === void 0 ? void 0 : _b.statusCodes[0];
                                var leadNum = parseInt(responseCode[0]);
                                // will use enum when it can, fall back to casting int when it can't
                                yield 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], eventListener));
                            }
                            else {
                                yield codegen_csharp_5.DefaultCase($this.responsesEmitter($this, opMethod, [resp], eventListener));
                            }
                        }
                        // missing default response?
                        if (!opMethod.operation.exceptions) {
                            // if no default, we need one that handles the rest of the stuff.
                            yield codegen_csharp_5.TerminalDefaultCase(function* () {
                                yield `throw new ${clientruntime_1.ClientRuntime.fullName}.UndeclaredResponseException(_response);`;
                            });
                        }
                    });
                };
                // try statements
                const sendTask = 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);
                yield `${response.value} = await ${sendTask.value};`;
                yield eventListener.signal(clientruntime_1.ClientRuntime.Events.ResponseCreated, response.value);
                const EOL = 'EOL';
                // LRO processing (if appropriate)
                if ((_a = $this.opMethod.operation.language.csharp) === null || _a === void 0 ? void 0 : _a.lro) {
                    yield '// this operation supports x-ms-long-running-operation';
                    const originalUri = codegen_csharp_12.Local('_originalUri', new codegen_csharp_2.LiteralExpression(`${reqParameter.use}.RequestUri.AbsoluteUri`));
                    yield originalUri;
                    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 = codegen_csharp_12.Local('_finalUri', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Location')));
                            yield finalUri;
                            break;
                        case 'azure-asyncoperation':
                        case 'azure-async-operation':
                            //depending on the type of request, do the appropriate behavior
                            switch ((_c = (_b = $this.opMethod.operation.requests) === null || _b === void 0 ? void 0 : _b[0].protocol.http) === null || _c === void 0 ? void 0 : _c.method.toLowerCase()) {
                                case 'post':
                                case 'delete':
                                    finalUri = 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 ((_d = $this.opMethod.operation.requests[0].protocol.http) === null || _d === void 0 ? void 0 : _d.method.toLowerCase()) {
                                    case 'post':
                                    case 'delete':
                                        finalUri = 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 = codegen_csharp_12.Local('asyncOperation', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Azure-AsyncOperation')));
                    yield asyncOperation;
                    const location = codegen_csharp_12.Local('location', response.invokeMethod('GetFirstHeader', new codegen_csharp_2.StringExpression('Location')));
                    yield location;
                    yield 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 EOL;
                        yield '// get the delay before polling. (default to 30 seconds if not present)';
                        yield `int delay = (int)(${response.value}.Headers.RetryAfter?.Delta?.TotalSeconds ?? 30);`;
                        // yield If(`!int.TryParse( ${response.invokeMethod('GetFirstHeader', new StringExpression(`Retry-After`)).value}, out int delay)`, `delay = 30;`);
                        yield eventListener.signal(clientruntime_1.ClientRuntime.Events.DelayBeforePolling, '$"Delaying {delay} seconds before polling."', response.value);
                        yield EOL;
                        yield '// start the delay timer (we\'ll await later...)';
                        const waiting = codegen_csharp_12.Local('waiting', new codegen_csharp_2.LiteralExpression(`${codegen_csharp_13.System.Threading.Tasks.Task()}.Delay(delay * 1000, ${$this.opMethod.contextParameter}.Token )`));
                        yield waiting;
                        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 '}';
                        const uriLocal = codegen_csharp_12.Local('_uri', codegen_csharp_14.Ternery(codegen_csharp_13.System.String.IsNullOrEmpty(asyncOperation), codegen_csharp_14.Ternery(codegen_csharp_13.System.String.IsNullOrEmpty(location), originalUri, 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 = codegen_csharp_12.Local('content', new codegen_csharp_2.LiteralExpression(`await ${response.value}.Content.ReadAsStringAsync()`));
                        yield content;
                        yield 'await waiting;';
                        yield EOL;
                        yield '// check for cancellation';
                        yield `if( ${$this.opMethod.contextParameter}.Token.IsCancellationRequested ) { return; }`;
                        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(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 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':
                                // perform a final GET on the specified final URI.
                                yield $this.finalGet(eventListener, finalUri, reqParameter, response);
                                break;
                            default:
                                yield codegen_csharp_7.If(`!string.IsNullOrWhiteSpace(${finalUri})`, function* () {
                                    yield $this.finalGet(eventListener, finalUri, reqParameter, response);
                                });
                                break;
                        }
                    });
                }
                yield responder();
            });
            yield 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 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);
    }
    *finalGet(eventListener, finalLocation, reqParameter, response) {
        yield '// create a new request with the final uri';
        yield reqParameter.assign(`${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(${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, eventListener) {
        var _a;
        if (linq_1.length(responses) > 1) {
            yield codegen_csharp_9.Switch('_contentType', function* () {
                var _a, _b, _c, _d;
                for (const eachResponse of linq_1.values(responses)) {
                    const mimetype = 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 = 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 = linq_1.length((_c = eachResponse.protocol.http) === null || _c === void 0 ? void 0 : _c.mediaTypes);
                    for (const mt of linq_1.values((_d = eachResponse.protocol.http) === null || _d === void 0 ? void 0 : _d.mediaTypes)) {
                        count--;
                        const mediaType = codemodel_v3_2.normalizeMediaType(mt);
                        if (mediaType) {
                            if (count === 0) {
                                yield codegen_csharp_5.Case(new codegen_csharp_2.StringExpression(mediaType).toString(), $this.NewResponseHandler(mimetype, eachResponse, callbackParameter));
                            }
                            else {
                                yield codegen_csharp_5.TerminalCase(new codegen_csharp_2.StringExpression(mediaType).toString(), '');
                            }
                        }
                    }
                }
            });
        }
        else {
            const response = responses[0];
            const callbackParameter = 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, '_response');
            yield $this.NewResponseHandler(linq_1.values((_a = response.protocol.http) === null || _a === void 0 ? void 0 : _a.mediaTypes).first() || '', response, callbackParameter);
        }
    }
    *responseHandlerForNormalPipeline(mimetype, eachResponse, callbackParameter) {
        const callbackParameters = new Array();
        if (callbackParameter.responseType) {
            // hande the body response
            const r = callbackParameter.responseType.deserializeFromResponse(codemodel_v3_2.knownMediaType(mimetype), codegen_csharp_2.toExpression('_response'), 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, codegen_csharp_2.toExpression('_response'), 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) {
        var _a;
        const callbackParameters = new Array();
        if (callbackParameter.responseType) {
            // hande the body response
            const r = callbackParameter.responseType.deserializeFromResponse(codemodel_v3_2.knownMediaType(mimetype), codegen_csharp_2.toExpression('_response'), 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, codegen_csharp_2.toExpression('_response'), 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)});`;
    }
    responseHandler(mimetype, eachResponse, callbackParameter) {
        return this.responseHandlerForNormalPipeline(mimetype, eachResponse, callbackParameter);
    }
    NewResponseHandler(mimetype, eachResponse, callbackParameter) {
        return this.NewResponseHandlerForNormalPipeline(mimetype, eachResponse, callbackParameter);
    }
}
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(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