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 namespace {{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 {
    public accessToken: string;

    applyToRequest(requestOptions: request.Options): void {
        requestOptions.headers["Authorization"] = "Bearer " + this.accessToken;
    }
}

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}} {
    protected basePath = '{{basePath}}';
    protected defaultHeaders : any = {};



    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(basePath?: string);
{{#authMethods}}
{{#isBasic}}
    constructor(username: string, password: string, basePath?: string);
{{/isBasic}}
{{/authMethods}}
    constructor(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}}

    set accessToken(token: string) {
        this.authentications.{{name}}.accessToken = token;
    }
{{/isOAuth}}
{{/authMethods}}
    private extendObj<T1,T2>(objA: T1, objB: T2) {
        for(let key in objB){
            if(objB.hasOwnProperty(key)){
                objA[key] = objB[key];
            }
        }
        return <T1&T2>objA;
    }
{{#operation}}
    /**
     * {{summary}}
     * {{notes}}
     {{#allParams}}* @param {{paramName}} {{description}}
     {{/allParams}}*/
    public {{nickname}} ({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) : Promise<{ response: http.ClientResponse; {{#returnType}}body: {{{returnType}}}; {{/returnType}}{{^returnType}}body?: any; {{/returnType}} }> {
        const path = this.basePath + '{{path}}'{{#pathParams}}
            .replace('{' + '{{baseName}}' + '}', String({{paramName}})){{/pathParams}};
        let queryParameters: any = {};
        let headerParams: any = this.extendObj({}, this.defaultHeaders);
        let 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['{{baseName}}'] = {{paramName}};
        }

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

{{/headerParams}}
        let useFormData = false;

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

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

        let 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}}
