import request = require('request');
import promise = require('bluebird');
import http = require('http');

// ===============================================
// This file is autogenerated - Please do not edit
// ===============================================

/* tslint:disable:no-unused-variable */

{{#models}}
{{#model}}
{{#description}}
/**
* {{{description}}}
*/
{{/description}}
export class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{
{{#vars}}
{{#description}}
    /**
    * {{{description}}}
    */
{{/description}}
    {{name}}: {{#isEnum}}{{classname}}.{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}};
{{/vars}}
}

{{#hasEnums}}
export module {{classname}} {
{{#vars}}
{{#isEnum}}
    export enum {{datatypeWithEnum}} { {{#allowableValues}}{{#values}}
        {{.}} = <any> '{{.}}',{{/values}}{{/allowableValues}}
    }
{{/isEnum}}
{{/vars}}
}
{{/hasEnums}}
{{/model}}
{{/models}}

interface Authentication {
    /**
    * Apply authentication settings to header and query params.
    */
    applyToRequest(requestOptions: request.Options): void;
}

class HttpBasicAuth implements Authentication {
    public username: string;
    public password: string;
    applyToRequest(requestOptions: request.Options): void {
        requestOptions.auth = {
            username: this.username, password: this.password
        }
    }
}

class ApiKeyAuth implements Authentication {
    public apiKey: string;

    constructor(private location: string, private paramName: string) {
    }

    applyToRequest(requestOptions: request.Options): void {
        if (this.location == "query") {
            (<any>requestOptions.qs)[this.paramName] = this.apiKey;
        } else if (this.location == "header") {
            requestOptions.headers[this.paramName] = this.apiKey;
        }
    }
}

class OAuth implements Authentication {
    applyToRequest(requestOptions: request.Options): void {
        // TODO: support oauth
    }
}

class VoidAuth implements Authentication {
    public username: string;
    public password: string;
    applyToRequest(requestOptions: request.Options): void {
        // Do nothing
    }
}

{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#description}}
/**
* {{&description}}
*/
{{/description}}
export class {{classname}} {
    private basePath = '{{contextPath}}';
    public authentications = {
        'default': <Authentication>new VoidAuth(),
{{#authMethods}}
{{#isBasic}}
        '{{name}}': new HttpBasicAuth(),
{{/isBasic}}
{{#isApiKey}}
        '{{name}}': new ApiKeyAuth({{#isKeyInHeader}}'header'{{/isKeyInHeader}}{{^isKeyInHeader}}'query'{{/isKeyInHeader}}, '{{keyParamName}}'),
{{/isApiKey}}
{{#isOAuth}}
        '{{name}}': new OAuth(),
{{/isOAuth}}
{{/authMethods}}
    }

    constructor(url: string, basePath?: string);
{{#authMethods}}
{{#isBasic}}
    constructor(url: string, username: string, password: string, basePath?: string);
{{/isBasic}}
{{/authMethods}}
    constructor(private url: string, basePathOrUsername: string, password?: string, basePath?: string) {
        if (password) {
{{#authMethods}}
{{#isBasic}}
            this.username = basePathOrUsername;
            this.password = password
{{/isBasic}}
{{/authMethods}}
            if (basePath) {
                this.basePath = basePath;
            }
        } else {
            if (basePathOrUsername) {
                this.basePath = basePathOrUsername
            }
        }
    }
{{#authMethods}}
{{#isBasic}}

    set username(username: string) {
        this.authentications.{{name}}.username = username;
    }

    set password(password: string) {
        this.authentications.{{name}}.password = password;
    }
{{/isBasic}}
{{#isApiKey}}

    set apiKey(key: string) {
        this.authentications.{{name}}.apiKey = key;
    }
{{/isApiKey}}
{{#isOAuth}}
{{/isOAuth}}
{{/authMethods}}
{{#operation}}

    public {{nickname}} ({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) : Promise<{ response: http.ClientResponse; {{#returnType}}body: {{{returnType}}}; {{/returnType}} }> {
        var path = this.url + this.basePath + '{{path}}';

{{#pathParams}}
        path = path.replace('{' + '{{paramName}}' + '}', String({{paramName}}));

{{/pathParams}}
        var queryParameters: any = {};
        var headerParams: any = {};
        var formParams: any = {};

{{#allParams}}
{{#required}}
        // verify required parameter '{{paramName}}' is set
        if (!{{paramName}}) {
            throw new Error('Missing required parameter {{paramName}} when calling {{nickname}}');
        }

{{/required}}
{{/allParams}}
{{#queryParams}}
        if ({{paramName}} !== undefined) {
            queryParameters['{{paramName}}'] = {{paramName}};
        }

{{/queryParams}}
{{#headerParams}}
        headerParams['{{paramName}}'] = {{paramName}};

{{/headerParams}}
        var useFormData = false;

{{#formParams}}
        if ({{paramName}} !== undefined) {
            formParams['{{paramName}}'] = {{paramName}};
        }
{{#isFile}}
        useFormData = true;
{{/isFile}}

{{/formParams}}
        var deferred = promise.defer<{ response: http.ClientResponse; {{#returnType}}body: {{{returnType}}}; {{/returnType}} }>();

        var requestOptions: request.Options = {
            method: '{{httpMethod}}',
            qs: queryParameters,
            headers: headerParams,
            uri: path,
            json: true,
{{#bodyParam}}
            body: {{paramName}},
{{/bodyParam}}
        }

{{#authMethods}}
        this.authentications.{{name}}.applyToRequest(requestOptions);

{{/authMethods}}
        this.authentications.default.applyToRequest(requestOptions);

        if (Object.keys(formParams).length) {
            if (useFormData) {
                (<any>requestOptions).formData = formParams;
            } else {
                requestOptions.form = formParams;
            }
        }

        request(requestOptions, (error, response, body) => {
            if (error) {
                deferred.reject(error);
            } else {
                if (response.statusCode >= 200 && response.statusCode <= 299) {
                    deferred.resolve({ response: response, body: body });
                } else {
                    deferred.reject({ response: response, body: body });
                }
            }
        });

        return deferred.promise;
    }
{{/operation}}
}
{{/operations}}
{{/apis}}
{{/apiInfo}}
