// This file contains all the non-ES6-standard helpers based on promise.

module.exports = {

    /**
     * A function that helps run functions under a concurrent limitation.
     * To run functions sequentially, use `yaku/lib/flow`.
     * @param  {Int} limit The max task to run at a time. It's optional.
     * Default is `Infinity`.
     * @param  {Iterable} list Any [iterable](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) object. It should be a lazy iteralbe object,
     * don't pass in a normal Array with promises.
     * @return {Promise}
     * @example
     * ```js
     * var kit = require('nokit');
     * var all = require('yaku/lib/all');
     *
     * var urls = [
     *     'http://a.com',
     *     'http://b.com',
     *     'http://c.com',
     *     'http://d.com'
     * ];
     * var tasks = function * () {
     *     var i = 0;
     *     yield kit.request(url[i++]);
     *     yield kit.request(url[i++]);
     *     yield kit.request(url[i++]);
     *     yield kit.request(url[i++]);
     * }();
     *
     * all(tasks).then(() => kit.log('all done!'));
     *
     * all(2, tasks).then(() => kit.log('max concurrent limit is 2'));
     *
     * all(3, { next: () => {
     *     var url = urls.pop();
     *     return {
     *          done: !url,
     *          value: url && kit.request(url)
     *     };
     * } })
     * .then(() => kit.log('all done!'));
     * ```
     */
    all: require("./all"),

    /**
     * Similar with the `Promise.race`, but only rejects when every entry rejects.
     * @param  {iterable} iterable An iterable object, such as an Array.
     * @return {Yaku}
     * @example
     * ```js
     * var any = require('yaku/lib/any');
     * any([
     *     123,
     *     Promise.resolve(0),
     *     Promise.reject(new Error("ERR"))
     * ])
     * .then((value) => {
     *     console.log(value); // => 123
     * });
     * ```
     */
    any: require("./any"),

    /**
     * Generator based async/await wrapper.
     * @param  {Generator} gen A generator function
     * @return {Yaku}
     * @example
     * ```js
     * var async = require('yaku/lib/async');
     * var sleep = require('yaku/lib/sleep');
     *
     * var fn = async(function * () {
     *     return yield sleep(1000, 'ok');
     * });
     *
     * fn().then(function (v) {
     *     console.log(v);
     * });
     * ```
     */
    async: require("./async"),

    /**
     * If a function returns promise, convert it to
     * node callback style function.
     * @param  {Function} fn
     * @param  {Any} self The `this` to bind to the fn.
     * @return {Function}
     */
    callbackify: require("./callbackify"),

    /**
     * **deprecate** Create a `jQuery.Deferred` like object.
     * It will cause some buggy problems, please don't use it.
     */
    Deferred: require("./Deferred"),

    /**
     * Creates a function that is the composition of the provided functions.
     * See `yaku/lib/async`, if you need concurrent support.
     * @param  {Iterable} list Any [iterable](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) object. It should be a lazy iteralbe object,
     * don't pass in a normal Array with promises.
     * @return {Function} `(val) -> Promise` A function that will return a promise.
     * @example
     * It helps to decouple sequential pipeline code logic.
     * ```js
     * var kit = require('nokit');
     * var flow = require('yaku/lib/flow');
     *
     * function createUrl (name) {
     *     return "http://test.com/" + name;
     * }
     *
     * function curl (url) {
     *     return kit.request(url).then((body) => {
     *         kit.log('get');
     *         return body;
     *     });
     * }
     *
     * function save (str) {
     *     kit.outputFile('a.txt', str).then(() => {
     *         kit.log('saved');
     *     });
     * }
     *
     * var download = flow(createUrl, curl, save);
     * // same as "download = flow([createUrl, curl, save])"
     *
     * download('home');
     * ```
     * @example
     * Walk through first link of each page.
     * ```js
     * var kit = require('nokit');
     * var flow = require('yaku/lib/flow');
     *
     * var list = [];
     * function iter (url) {
     *     return {
     *         done: !url,
     *         value: url && kit.request(url).then((body) => {
     *             list.push(body);
     *             var m = body.match(/href="(.+?)"/);
     *             if (m) return m[0];
     *         });
     *     };
     * }
     *
     * var walker = flow(iter);
     * walker('test.com');
     * ```
     */
    flow: require("./flow"),

    /**
     * Enable a helper to catch specific error type.
     * It will be directly attach to the prototype of the promise.
     * @param  {class}    type
     * @param  {Function} onRejected
     * @return {Promise}
     * ```js
     * var Promise = require('yaku');
     * require('yaku/lib/guard');
     *
     * class AnError extends Error {
     * }
     *
     * Promise.reject(new AnError('hey'))
     * .guard(AnError, (err) => {
     *      // only log AnError type
     *      console.log(err);
     * })
     * .then(() => {
     *      console.log('done');
     * })
     * .guard(Error, (err) => {
     *      // log all error type
     *      console.log(err)
     * });
     * ```
     */
    guard: require("./guard"),

    /**
     * if-else helper
     * @param  {Promise} cond
     * @param  {Function} trueFn
     * @param  {Function} falseFn
     * @return {Promise}
     * @example
     * ```js
      * var Promise = require('yaku');
      * var yutils = require('yaku/lib/utils');
      *
      * yutils.if(Promise.resolve(false), () => {
      *     // true
      * }, () => {
      *     // false
      * })
     * ```
     */
    "if": require("./if"),

    /**
     * **deprecate** Check if an object is a promise-like object.
     * Don't use it to coercive a value to Promise, instead use `Promise.resolve`.
     * @param  {Any}  obj
     * @return {Boolean}
     */
    isPromise: require("./isPromise"),

    /**
     * Create a promise that never ends.
     * @return {Promise} A promise that will end the current pipeline.
     */
    never: require("./never"),

    /**
     * Convert a node callback style function to a function that returns
     * promise when the last callback is not supplied.
     * @param  {Function} fn
     * @param  {Any} self The `this` to bind to the fn.
     * @return {Function}
     * @example
     * ```js
     * var promisify = require('yaku/lib/promisify');
     * function foo (val, cb) {
     *     setTimeout(() => {
     *         cb(null, val + 1);
     *     });
     * }
     *
     * var bar = promisify(foo);
     *
     * bar(0).then((val) => {
     *     console.log val // output => 1
     * });
     *
     * // It also supports the callback style.
     * bar(0, (err, val) => {
     *     console.log(val); // output => 1
     * });
     * ```
     */
    promisify: require("./promisify"),

    /**
     * Create a promise that will wait for a while before resolution.
     * @param  {Integer} time The unit is millisecond.
     * @param  {Any} val What the value this promise will resolve.
     * @return {Promise}
     * @example
     * ```js
     * var sleep = require('yaku/lib/sleep');
     * sleep(1000).then(() => console.log('after one second'));
     * ```
     */
    sleep: require("./sleep"),

    /**
     * Read the `Observable` section.
     * @type {Function}
     */
    Observable: require("./Observable"),

    /**
     * Retry a function until it resolves before a mount of times, or reject with all
     * the error states.
     * @version_added v0.7.10
     * @param  {Number | Function} countdown How many times to retry before rejection.
     * @param  {Number} span Optional. How long to wait before each retry in millisecond.
     * When it's a function `(errs) => Boolean | Promise.resolve(Boolean)`,
     * you can use it to create complex countdown logic,
     * it can even return a promise to create async countdown logic.
     * @param  {Function} fn The function can return a promise or not.
     * @param  {Any} this Optional. The context to call the function.
     * @return {Function} The wrapped function. The function will reject an array
     * of reasons that throwed by each try.
     * @example
     * Retry 3 times before rejection, wait 1 second before each retry.
     * ```js
     * var retry = require('yaku/lib/retry');
     * var { request } = require('nokit');
     *
     * retry(3, 1000, request)('http://test.com').then(
     *    (body) => console.log(body),
     *    (errs) => console.error(errs)
     * );
     * ```
     * @example
     * Here a more complex retry usage, it shows an random exponential backoff algorithm to
     * wait and retry again, which means the 10th attempt may take 10 minutes to happen.
     * ```js
     * var retry = require('yaku/lib/retry');
     * var sleep = require('yaku/lib/sleep');
     * var { request } = require('nokit');
     *
     * function countdown (retries) {
     *    var attempt = 0;
     *    return async () => {
     *         var r = Math.random() * Math.pow(2, attempt) * 1000;
     *         var t = Math.min(r, 1000 * 60 * 10);
     *         await sleep(t);
     *         return attempt++ < retries;
     *    };
     * }
     *
     * retry(countdown(10), request)('http://test.com').then(
     *    (body) => console.log(body),
     *    (errs) => console.error(errs)
     * );
     * ```
     */
    retry: require("./retry"),

    /**
     * Throw an error to break the program.
     * @param  {Any} err
     * @example
     * ```js
     * var ythrow = require('yaku/lib/throw');
     * Promise.resolve().then(() => {
     *     // This error won't be caught by promise.
     *     ythrow('break the program!');
     * });
     * ```
     */
    "throw": require("./throw")
};
