/**************************************************************************************************
 *
 * ADOBE SYSTEMS INCORPORATED
 * Copyright 2015 Adobe Systems Incorporated
 * All Rights Reserved.
 *
 * NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the
 * terms of the Adobe license agreement accompanying it.  If you have received this file from a
 * source other than Adobe, then your use, modification, or distribution of it requires the prior
 * written permission of Adobe.
 *
 **************************************************************************************************/

/*jslint vars: true, node: true, plusplus: true, unparam: true, nomen: true */
/*global GLOBAL */
var ffi = require('ffi');

module.exports.ResponseCollector = function ResponseCollector(length, finish) {
    'use strict';
    this.responses = [];
    this.errors = [];
    this.pending = length;
    this.collect = function (index, err, data) {
        this.responses[index] = data;
        this.errors[index] = err;
        --this.pending;
        if (this.pending === 0 && finish) {
            finish(this.errors, this.responses);
        }
    };
};

module.exports.stripNonExistant = function stripNonExistant(object) {
    'use strict';
    var returnVal = {};
    var key;
    for (key in object) {
        if (object.hasOwnProperty(key)) {
            if (object[key]) {
                returnVal[key] = object[key];
            }
        }
    }
    return returnVal;
};


module.exports.setLogger = function setLogger(logger) {
    'use strict';
    module.exports.log = logger;
};

module.exports.logLevel = {
    INFO: 2,
    WARNING: 1,
    ERROR: 0
};

module.exports.log = console.log;

module.exports.throttle = function throttle(func, wait, options) {
    'use strict';
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) {
        options = {};
    }
    var later = function () {
        previous = options.leading === false ? 0 : Date.now();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) {
            context = args = null;
        }
    };
    return function () {
        var now = Date.now();
        if (!previous && options.leading === false) {
            previous = now;
        }
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout) {
                context = args = null;
            }
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
        return result;
    };
};

module.exports.wrapWithPostCall = function (original, postCall) {
    'use strict';
    return function () {
        var returnVal = original.apply(this, arguments);
        postCall.call(this);
        return returnVal;
    };
};

/**
    Handling callbacks - we need a wrapper to avoid things getting garbage collected
    Workaround for https://github.com/node-ffi/node-ffi/issues/241
**/

var callbacks = [];

function createHTTPCallbackFn(callback) {
    'use strict';
    var cb = ffi.Callback('void', ['CString'], callback);
    callbacks.push(cb);
    return cb;
}

function releaseHTTPCallbackFn(cb) {
    'use strict';
    var index = callbacks.indexOf(cb);
    if (index !== -1) {
        callbacks.splice(index, 1);
    }
}


module.exports.wrapNativeCallback = function (original) {
    'use strict';
    return function () {
        var args = Array.prototype.slice.call(arguments);
        //Assuming that the last argument is callback.
        var callback = args[args.length - 1];
        var cb = createHTTPCallbackFn(function (status, data) {
            // Note: releaseHTTPCallbackFn just removes the reference to the callback, allowing it to be
            // garbage collected. It doesn't call into IMSLib, so it's safe to do during the callback.
            releaseHTTPCallbackFn(cb);
            callback(status, data);
        });
        args[args.length - 1] = cb;
        original.apply(this, args);
    };
};
