'use strict';

const async = require('async');
const _ = require('underscore');

module.exports = class MessageManager {

    constructor(scope, messageEmitter) {
        this.scope = scope;
        this.logger = scope.logger;
        this.scope.nodeList = [];
        this.scope.nodeInfo = {};
        this.messageEmitter = messageEmitter;
    }

    init(callback) {
        let scope = this.scope,
            logger = scope.logger;
        async.parallel({
            nodeList : scope.publisher.get.bind(scope.publisher, 'nodelist'),
            nodeInfo : scope.publisher.get.bind(scope.publisher, 'nodeinfo')
        }, (error, result) => {
            if(result.nodeList) {
                try {
                    scope.nodeList = JSON.parse(result.nodeList);
                    scope.nodeInfo = JSON.parse(result.nodeInfo);
                } catch(e) {
                    scope.nodeList = []
                    scope.nodeInfo = {}
                }
            } else {
                scope.nodeList = []
                scope.nodeInfo = {}
            }

            let isMaster = false;
            if(!scope.nodeList.length) {
                isMaster = true;
                logger.info('start message generator');
            } else {
                console.log('start message listener');
                console.log('new node');
                scope.publisher.publish('newnode', JSON.stringify({node: scope.nodeName}));
            }
            scope.nodeList.push(scope.nodeName);
            scope.nodeInfo[scope.nodeName] = isMaster;
            console.log(scope.nodeInfo);
            scope.publisher.set('nodelist', JSON.stringify(scope.nodeList));
            scope.publisher.set('nodeinfo', JSON.stringify(scope.nodeInfo));
            callback(error);
        })
    }

    getClosestNode() {
        let nodeList = this.scope.nodeList,
            nodeName = this.scope.nodeName;
        let myIndex = _.indexOf(nodeList, nodeName);
        return myIndex + 1 == nodeList.length ? nodeList[0] : nodeList[myIndex + 1];
    }

    watchClosestNodeStatus() {
        this.messageEmitter.subscribe([`${this.scope.nodeName}:pong`], () => {
            async.forever(this._tryToPingNextNode.bind(this))
        });
    }

    _tryToPingNextNode(next) {
        let scope = this.scope,
            nodeName = scope.nodeName,
            nodeList = scope.nodeList,
            nodeInfo = scope.nodeInfo,
            publisher = scope.publisher,
            messageEmitter = this.messageEmitter;

        let nextNode = this.getClosestNode();
        if(nextNode === nodeName) {
            setTimeout(next, 1000);
        } else {
            let nextNodePingTimeout = setTimeout(() => {
                console.log('node dead', nextNode);
                nodeList = _.without(nodeList, nextNode);
                if(nodeInfo[nextNode]) {
                    nodeInfo[nodeName] = true;
                    logger.info(`new master is ${nodeName}`);
                    //start broadcast
                }
                delete nodeInfo[nextNode]; //check if nextNode is master

                console.log('current node list', nodeList);
                publisher.set('nodelist', JSON.stringify(nodeList));
                publisher.set('nodeinfo', JSON.stringify(nodeInfo));
                publisher.publish(`updatenodelist`, JSON.stringify({nodeList, nodeInfo}));
                next(null);
            }, 1000);
            console.log('ping node', nextNode);
            messageEmitter.once(`${nodeName}:pong`, () => {
                clearTimeout(nextNodePingTimeout);
                setTimeout(next, 1000);
            });
            publisher.publish(`${nextNode}:ping`, JSON.stringify({node: nodeName}));
        }
    }
}

@@ -152,24 +152,58 @@
 eEmitter) %7B%0A
+        this.pingInterval = 3000;%0A
         this
@@ -2321,108 +2321,76 @@
     
-let scope = this.scope,%0A            nodeName = scope.nodeName,%0A            nodeList = scope.nodeList
+console.log('try to ping next node');%0A        let scope = this.scope
 ,%0A  
@@ -2403,20 +2403,20 @@
     node
-Info
+Name
  = scope
@@ -2420,20 +2420,20 @@
 ope.node
-Info
+Name
 ,%0A      
@@ -2515,16 +2515,62 @@
 eEmitter
+,%0A            pingInterval = this.pingInterval
 ;%0A%0A     
@@ -2667,36 +2667,44 @@
 etTimeout(next, 
-1000
+pingInterval
 );%0A        %7D els
@@ -2699,32 +2699,187 @@
        %7D else %7B%0A
+            let onPongCallback = () =%3E %7B%0A                clearTimeout(nextNodePingTimeout);%0A                setTimeout(next, pingInterval);%0A            %7D;%0A
             let 
@@ -2979,32 +2979,120 @@
                 
+messageEmitter.removeListener(%60$%7BnodeName%7D:pong%60,onPongCallback);%0A                scope.
 nodeList = _.wit
@@ -3096,16 +3096,22 @@
 without(
+scope.
 nodeList
@@ -3142,16 +3142,22 @@
      if(
+scope.
 nodeInfo
@@ -3182,32 +3182,38 @@
                 
+scope.
 nodeInfo%5BnodeNam
@@ -3364,16 +3364,22 @@
  delete 
+scope.
 nodeInfo
@@ -3470,16 +3470,22 @@
  list', 
+scope.
 nodeList
@@ -3540,24 +3540,30 @@
 N.stringify(
+scope.
 nodeList));%0A
@@ -3619,16 +3619,22 @@
 ringify(
+scope.
 nodeInfo
@@ -3709,26 +3709,152 @@
 fy(%7B
-nodeList, nodeInfo
+%0A                    node: nodeName,%0A                    nodeList: scope.nodeList,%0A                    nodeInfo: scope.nodeInfo%0A                
 %7D));
@@ -3897,20 +3897,28 @@
      %7D, 
-1000
+pingInterval
 );%0A     
@@ -4016,120 +4016,22 @@
 g%60, 
-() =%3E %7B%0A                clearTimeout(nextNodePingTimeout);%0A                setTimeout(next, 1000);%0A            %7D
+onPongCallback
 );%0A 

fix bugs