/**
 * @author Titus Wormer
 * @copyright 2015 Titus Wormer
 * @license MIT
 * @module attach-ware
 * @fileoverview Middleware with configuration.
 * @example
 *   var ware = require('attach-ware')(require('ware'));
 *
 *   var middleware = ware()
 *     .use(function (context, options) {
 *         if (!options.condition) return;
 *
 *         return function (req, res, next) {
 *           res.x = 'hello';
 *           next();
 *         };
 *     }, {
 *         'condition': true
 *     })
 *     .use(function (context, options) {
 *         if (!options.condition) return;
 *
 *         return function (req, res, next) {
 *           res.y = 'world';
 *           next();
 *         };
 *     }, {
 *         'condition': false
 *     });
 *
 *   middleware.run({}, {}, function (err, req, res) {
 *     res.x; // "hello"
 *     res.y; // undefined
 *   });
 */

'use strict';

/* eslint-env commonjs */

var slice = [].slice;
var unherit = require('unherit');

/**
 * Clone `Ware` without affecting the super-class and
 * turn it into configurable middleware.
 *
 * @param {Function} Ware - Ware-like constructor.
 * @return {Function} AttachWare - Configurable middleware.
 */
function patch(Ware) {
    /*
     * Methods.
     */

    var useFn = Ware.prototype.use;

    /**
     * @constructor
     * @class {AttachWare}
     */
    var AttachWare = unherit(Ware);

    AttachWare.prototype.foo = true;

    /**
     * Attach configurable middleware.
     *
     * @memberof {AttachWare}
     * @this {AttachWare}
     * @param {Function} attach - Attacher.
     * @return {AttachWare} - `this`.
     */
    function use(attach) {
        var self = this;
        var params = slice.call(arguments, 1);
        var index;
        var length;
        var fn;

        /*
         * Accept other `AttachWare`.
         */

        if (attach instanceof AttachWare) {
            if (attach.attachers) {
                return self.use(attach.attachers);
            }

            return self;
        }

        /*
         * Accept normal ware.
         */

        if (attach instanceof Ware) {
            self.fns = self.fns.concat(attach.fns);
            return self;
        }

        /*
         * Multiple attachers.
         */

        if ('length' in attach && typeof attach !== 'function') {
            index = -1;
            length = attach.length;

            while (++index < length) {
                self.use.apply(self, [attach[index]].concat(params));
            }

            return self;
        }

        /*
         * Single attacher.
         */

        fn = attach.apply(null, [self.context || self].concat(params));

        /*
         * Store the attacher to not break `new Ware(otherWare)`
         * functionality.
         */

        if (!self.attachers) {
            self.attachers = [];
        }

        self.attachers.push(attach);

        /*
         * Pass `fn` to the original `Ware#use()`.
         */

        if (fn) {
            useFn.call(self, fn);
        }

        return self;
    }

    AttachWare.prototype.use = use;

    return function (fn) {
        return new AttachWare(fn);
    };
}

module.exports = patch;
