// Timeout cached values

'use strict';

var aFrom = require('es5-ext/array/from')
    , noop = require('es5-ext/function/noop')
    , forEach = require('es5-ext/object/for-each')
    , timeout = require('timers-ext/valid-timeout')
    , extensions = require('../lib/registered-extensions')

    , max = Math.max, min = Math.min, create = Object.create;

extensions.maxAge = function (maxAge, conf, options) {
    var timeouts, postfix, preFetchAge, preFetchTimeouts;

    maxAge = timeout(maxAge);
    if (!maxAge) return;

    timeouts = create(null);
    postfix = (options.async && extensions.async) ? 'async' : '';
    conf.on('set' + postfix, function (id) {
        timeouts[id] = setTimeout(function () {
            conf.delete(id);
        }, maxAge);
        if (!preFetchTimeouts) return;
        if (preFetchTimeouts[id]) clearTimeout(preFetchTimeouts[id]);
        preFetchTimeouts[id] = setTimeout(function () {
            delete preFetchTimeouts[id];
        }, preFetchAge);
    });
    conf.on('delete' + postfix, function (id) {
        clearTimeout(timeouts[id]);
        delete timeouts[id];
        if (!preFetchTimeouts) return;
        clearTimeout(preFetchTimeouts[id]);
        delete preFetchTimeouts[id];
    });

    if (options.preFetch) {
        if ((options.preFetch === true) || isNaN(options.preFetch)) {
            preFetchAge = 0.333;
        } else {
            preFetchAge = max(min(Number(options.preFetch), 1), 0);
        }
        if (preFetchAge) {
            preFetchTimeouts = {};
            preFetchAge = (1 - preFetchAge) * maxAge;
            conf.on('get' + postfix, function (id, args, context) {
                if (!preFetchTimeouts[id]) {
                    preFetchTimeouts[id] = setTimeout(function () {
                        delete preFetchTimeouts[id];
                        conf.delete(id);
                        if (options.async) {
                            args = aFrom(args);
                            args.push(noop);
                        }
                        conf.memoized.apply(context, args);
                    }, 0);
                }
            });
        }
    }

    conf.on('clear' + postfix, function () {
        forEach(timeouts, function (id) {
            clearTimeout(id);
        });
        timeouts = {};
        if (preFetchTimeouts) {
            forEach(preFetchTimeouts, function (id) {
                clearTimeout(id);
            });
            preFetchTimeouts = {};
        }
    });
};
