/*
    On first heartbeat, add host to list.
    Periodically sweep to expire.
    On missing host, fire alert.

{
    name: 'heartbeat',
    ttl: 16000,
    host: 'example-1',
    time: 1401651649819
}

*/


var
    _       = require('lodash'),
    assert  = require('assert'),
    bole    = require('bole'),
    stream  = require('stream'),
    TTLPool = require('ttl-pool'),
    util    = require('util')
    ;

var WARN_THRESHOLD = 0;

var Heartbeat = module.exports = function Heartbeat()
{
    stream.Writable.call(this, { objectMode: true });

    this.pool = TTLPool(this.handleExpire.bind(this));
    this.hosts = {};
    this.log = bole('heartbeat');
};
util.inherits(Heartbeat, stream.Writable);

Heartbeat.prototype.pool = null;
Heartbeat.prototype.hosts = null;

Heartbeat.prototype._write = function _write(event, encoding, callback)
{
    switch (event.name)
    {
    case 'heartbeat':
        return this.handleHeartbeat(event, callback);

    case 'shutdown':
        return this.handleShutdown(event, callback);
    }

    callback();
};

Heartbeat.prototype.handleHeartbeat = function handleHeartbeat(event, callback)
{
    // This one's fun.
    // If host skips a beat (for example, a temporary freeze), `TTLPool` will
    // remove it from its internal dictionary. That would be fine, if
    // `TTLPool#ping` implicitely added it back, but it doesn't. Thus, we
    // call both `TTLPool#ping` and `TTLPool#add` in order to make sure that
    // the entry is actually in there.
    this.pool.add(event.host, event.ttl);

    if (this.hosts[event.host])
    {
        this.pool.ping(event.host, event.ttl);
        this.hosts[event.host].skipped = 0;
    }
    else
    {
        this.log.info('now tracking heartbeats for ' + event.host);
        this.hosts[event.host] = event;
        event.skipped = 0;
    }

    callback();
};

Heartbeat.prototype.handleShutdown = function handleShutdown(event, callback)
{
    this.pool.remove(event.host);
    delete this.hosts[event.host];
    this.log.info('graceful shutdown for ' + event.host);

    callback();
};

Heartbeat.prototype.handleExpire = function handleExpire(host, explicit)
{
    if (explicit) return;

    var record = this.hosts[host];
    record.skipped++;

    if (record.skipped > WARN_THRESHOLD)
    {
        this.log.warn(host + ' heartbeat not heard for ' + record.skipped + ' beats!');
    }
};

@@ -609,18 +609,23 @@
   this.h
-os
+eartbea
 ts = %7B%7D;
@@ -764,114 +764,370 @@
 pe.h
-osts = null;%0A%0AHeartbeat.prototype._write = function _write(event, encoding, callback)%0A%7B%0A    switch (event.
+eartbeats = null;%0A%0AHeartbeat.prototype._getHeartbeatName = function(event)%0A%7B%0A    var split = event.name.split('.');%0A    return %5Bevent.host%5D.concat(split.slice(0, split.length - 1)).join('.');%0A%7D;%0A%0AHeartbeat.prototype._write = function _write(event, encoding, callback)%0A%7B%0A    var split = event.name.split('.');%0A    var name = split%5Bsplit.length - 1%5D;%0A%0A    switch (
 name
@@ -1751,24 +1751,80 @@
 y in there.%0A
+    var heartbeatName = this._getHeartbeatName(event);%0A%0A
     this.poo
@@ -1829,26 +1829,29 @@
 ool.add(
-event.host
+heartbeatName
 , event.
@@ -1871,31 +1871,39 @@
  (this.h
-osts%5Bevent.host
+eartbeats%5BheartbeatName
 %5D)%0A    %7B
@@ -1926,26 +1926,29 @@
 ol.ping(
-event.host
+heartbeatName
 , event.
@@ -1967,31 +1967,39 @@
   this.h
-osts%5Bevent.host
+eartbeats%5BheartbeatName
 %5D.skippe
@@ -2081,26 +2081,29 @@
 for ' + 
-event.host
+heartbeatName
 );%0A     
@@ -2111,31 +2111,39 @@
   this.h
-osts%5Bevent.host
+eartbeats%5BheartbeatName
 %5D = even
@@ -2275,24 +2275,79 @@
 callback)%0A%7B%0A
+    var heartbeatName = this._getHeartbeatName(event);%0A
     this.poo
@@ -2355,26 +2355,29 @@
 .remove(
-event.host
+heartbeatName
 );%0A    d
@@ -2392,23 +2392,31 @@
 is.h
-osts%5Bevent.host
+eartbeats%5BheartbeatName
 %5D;%0A 
@@ -2459,26 +2459,29 @@
 for ' + 
-event.host
+heartbeatName
 );%0A%0A    
@@ -2554,19 +2554,28 @@
 Expire(h
-ost
+eartbeatName
 , explic
@@ -2634,17 +2634,31 @@
 is.h
-osts%5Bhost
+eartbeats%5BheartbeatName
 %5D;%0A 
@@ -2749,19 +2749,28 @@
 g.warn(h
-ost
+eartbeatName
  + ' hea

Generalize heartbeats