'use strict';

var $npm = {
    utils: require('./utils'),
    batch: require('./ext/batch'),
    page: require('./ext/page'),
    sequence: require('./ext/sequence'),
    stream: {
        read: require('./ext/stream/read')
    }
};

/**
 * @module spex
 * @summary Specialized Promise Extensions
 * @author Vitaly Tomilov
 *
 * @description
 * Attaches to an external promise library and provides additional methods built solely
 * on the basic promise operations:
 *  - construct a new promise with a callback function
 *  - resolve a promise with some result data
 *  - reject a promise with a reason
 *
 * ### usage
 * For any third-party promise library:
 * ```js
 * var promise = require('bluebird');
 * var spex = require('spex')(promise);
 * ```
 * For ES6 promises:
 * ```js
 * var spex = require('spex')(Promise);
 * ```
 *
 * @param {Object|Function} promiseLib
 * Instance of a promise library to be used by this module.
 *
 * Some implementations use `Promise` constructor to create a new promise, while
 * others use the module's function for it. Both types are supported the same.
 *
 * Alternatively, an object of type $[PromiseAdapter] can be passed in, which provides
 * compatibility with any promise library outside of the standard.
 *
 * Passing in a promise library that cannot be recognized will throw
 * `Invalid promise library specified.`
 *
 * @returns {Object}
 * Namespace with all supported methods.
 *
 * @see $[PromiseAdapter], $[batch], $[page], $[sequence], $[stream]
 */
function main(promiseLib) {
    
    var spex = {}, // library instance;
        promise = parsePromiseLib(promiseLib); // promise library parsing;
    
    var config = {
        spex: spex,
        promise: promise,
        utils: $npm.utils(promise)
    };
    
    spex.batch = $npm.batch(config);
    spex.page = $npm.page(config);
    spex.sequence = $npm.sequence(config);
    
    spex.stream = {};
    spex.stream.read = $npm.stream.read(config);
    
    config.utils.extend(spex, '$p', promise);
    
    Object.freeze(spex.stream);
    Object.freeze(spex);

    return spex;
}

//////////////////////////////////////////
// Parses and validates a promise library;
function parsePromiseLib(lib) {
    if (lib) {
        var promise;
        if (lib instanceof main.PromiseAdapter) {
            promise = function (func) {
                return lib.create(func);
            };
            promise.resolve = lib.resolve;
            promise.reject = lib.reject;
            return promise;
        }
        var t = typeof lib;
        if (t === 'function' || t === 'object') {
            var root = lib.Promise instanceof Function ? lib.Promise : lib;
            promise = function (func) {
                return new root(func);
            };
            promise.resolve = root.resolve;
            promise.reject = root.reject;
            if (promise.resolve instanceof Function && promise.reject instanceof Function) {
                return promise;
            }
        }
    }
    throw new TypeError("Invalid promise library specified.");
}

main.PromiseAdapter = require('./adapter');
Object.freeze(main);

module.exports = main;
