// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var test = require('tape');
var util = require('util');
var TimeMock = require('time-mock');

var TimeHeap = require('../time_heap');

permute([1, 2, 3, 4, 5, 6, 7], testWithTimes);

function testWithTimes(times) {
    test('TimeHeap Works for: ' + times, function t(assert) {
        var items = createTestTimeoutItems(times);

        var timers = TimeMock(1);
        var heap = new TimeHeap({
            timers: timers
        });

        var now = timers.now();
        var i = 0;
        for (i = 0; i < items.length; i++) {
            heap.update(items[i], now);
        }

        items.sort(function cmp(a, b) {
            return a.timeout - b.timeout;
        });

        var last = 0;
        for (i = 0; i < items.length; i++) {
            var item = items[i];
            var desc = util.format('items[%s]: %s', i, item.name);
            var elapse = item.timeout - last;
            assert.ok(!item.timedOut, desc + ' not yet timed out @t' + now);
            now += elapse;
            timers.advance(elapse);
            heap.callExpiredTimeouts(now); // in lieu of defering through process.nextTick
            assert.ok(item.timedOut, desc + ' timed out out @t' + now);
            last = item.timeout;
        }

        assert.end();
    });
}

function TestTimeoutItem(t, name) {
    var self = this;
    self.timeout = t;
    self.name = name;
    self.timedOut = 0;
}

TestTimeoutItem.prototype.onTimeout = function onTimeout(now) {
    var self = this;
    self.timedOut = now;
};

function createTestTimeoutItems(times) {
    return times.map(function each(t) {
        var name = String.fromCharCode('a'.charCodeAt(0) + t);
        return new TestTimeoutItem(t, name);
    });
}

function permute(letters, iter) {
    if (letters.length <= 0) {
        iter(letters);
        return;
    }

    letters.forEach(function eachHead(head, i) {
        if (i > 0) {
            letters[i] = letters[0];
            letters[0] = head;
        }

        var tail = letters.slice(1);
        permute(tail, function eachSubPerm(subPerm) {
            subPerm.unshift(head);
            iter(subPerm);
            // subPerm.shift();
        });

        if (i > 0) {
            letters[0] = letters[i];
            letters[i] = head;
        }
    });
}

@@ -1227,191 +1227,240 @@
 ');%0A
-%0Avar TimeHeap = require('../time_heap');%0A%0Apermute(%5B1, 2, 3, 4, 5, 6, 7%5D, testWithTimes);%0A%0Afunction testWithTimes(times) %7B%0A    test('TimeHeap Works for: ' + times, function t(assert) %7B
+var TestSearch = require('./lib/test_search');%0A%0Avar TimeHeap = require('../time_heap');%0A%0Atest('TimeHeap works for all 7-permutations', function t(assert) %7B%0A%0A    function testWithTimes(state, assert) %7B%0A        var times = state.perm;
 %0A   
@@ -2437,36 +2437,709 @@
 ert.end();%0A    %7D
+%0A%0A    var perms = %5B%5D;%0A    permute(%5B1, 2, 3, 4, 5, 6, 7%5D, function each(perm) %7B%0A        perms.push(perm);%0A    %7D);%0A%0A    (new TestSearch(%7B%0A        silentPass: true,%0A        first: true,%0A%0A        init: function init() %7B%0A            this.frontier.push(%7B%0A                permId: 0,%0A                perm: perms%5B0%5D%0A            %7D);%0A        %7D,%0A%0A        next: function next(state, _emit) %7B%0A            var nextPermId = state.permId + 1;%0A            if (nextPermId %3C perms.length) %7B%0A                _emit(%7B%0A                    permId: nextPermId,%0A                    perm: perms%5BnextPermId%5D%0A                %7D);%0A            %7D%0A        %7D,%0A%0A        test: testWithTimes%0A    %7D)).run(assert
 );%0A%7D
+);
 %0A%0Afunction TestT

refactor over TestSearch for less spam