import * as __ from 'underscore';
import * as  _nodeclient from './nodeclient.js';
import * as  _wc from './webclient.js';
import { logging, Logger } from '../common/logging';
import * as  stat from '../monitor/stat';
import * as  starter from './starter';
const io = require('socket.io');

let STATUS_INTERVAL = 60 * 1000; // 60 seconds
let HEARTBEAT_INTERVAL = 30 * 1000; // 30 seconds
let STATUS_IDLE = 0;
let STATUS_READY = 1;
let STATUS_RUNNING = 2;
let STATUS_DISCONN = 3;

export interface ServerCfg {
    clients: Array<any>;
    mainFile: string;
    master: { [key: string]: any };
    scriptFile: string;
    script: Array<any>;
}

/**
 *
 * robot master instance
 *
 * @param {Object} conf
 *
 * conf.main client run file
 */
export class Server {
    log: Logger;
    nodes: { [key: string]: any } = {};
    web_clients: { [key: string]:  _wc.WebClient } = {};
    conf: ServerCfg;
    runconfig = { maxuser: 1, agent: 1 };
    status: number = STATUS_RUNNING;
    io: any;
    constructor(conf: ServerCfg) {
        this.log = logging;
        this.conf = conf || {} as ServerCfg;
        setInterval(() => {
            this.log.info('Nodes: ' + __(this.nodes).size() + ', ' +
                'WebClients: ' + __(this.web_clients).size());
        }, STATUS_INTERVAL);
    }

    listen(port: number | string) {
        this.io = io.listen(port);
        this.register();
    }
    // Registers new Node with Server, announces to WebClients
    announce_node(socket: any, message: any) {
        let rserver = this, nodeId = message.nodeId;
        if (!!rserver.nodes[nodeId]) {
            this.log.warn('Warning: Node \'' + nodeId + '\' already exists, delete old items ');
            socket.emit('node_already_exists');
            delete rserver.nodes[nodeId];
        }
        else {
            this.log.warn('reg: Node \'' + nodeId + '\' ');
        }

        let node = new _nodeclient.NodeClient(nodeId, socket, this);
        rserver.nodes[nodeId] = node;

        __(rserver.web_clients).each(function (web_client) {
            web_client.add_node(node);
        });

        socket.on('disconnect', function () {
            delete rserver.nodes[nodeId];
            __(rserver.web_clients).each(function (web_client) {
                web_client.remove_node(node);
            });
            if (__.size(rserver.nodes) <= 0) {
                rserver.status = STATUS_IDLE;
            }
            stat.clear(nodeId);
        });

        socket.on('report', function (message: string) {
            stat.merge(nodeId, message);
        });

        /* temporary code */
        socket.on('error', function (message: Error) {
            __(rserver.web_clients).each(function (web_client) {
                web_client.error_node(node, message);
            });
        });
        socket.on('crash', function (message: Error) {
            __(rserver.web_clients).each(function (web_client) {
                web_client.error_node(node, message);
            });
            rserver.status = STATUS_READY;
        });
        /* temporary code */
    }
    // Registers new WebClient with Server
    announce_web_client(socket:any) {
        let rserver = this;
        let web_client = new _wc.WebClient(socket, rserver);
        rserver.web_clients[web_client.id] = web_client;
        __(rserver.nodes).each(function (node, nlabel) {
            web_client.add_node(node as any);
        });
        setInterval(function () {
            rserver.io.sockets.in('web_clients').emit('statusreport', { status: rserver.status });
        }, STATUS_INTERVAL / 10);
        socket.on('webreport', function (message: string) {
            if (rserver.status === STATUS_RUNNING) {
                socket.emit('webreport', rserver.runconfig.agent, rserver.runconfig.maxuser, stat.getTimeData(), stat.getCountData());
            }
        });

        socket.on('detailreport', function (message: string) {
            if (rserver.status === STATUS_RUNNING) {
                socket.emit('detailreport', stat.getDetails());
            }
        });

        socket.on('disconnect', function () {
            delete rserver.web_clients[web_client.id];
        });
    }

    // Register announcement, disconnect callbacks
    register() {
        let rserver = this;
        // rserver.io.set('log level', 1);
        rserver.io.sockets.on('connection', function (socket:any) {
            socket.on('announce_node', function (message: string) {
                rserver.log.info('Registering new node ' + JSON.stringify(message));
                rserver.announce_node(socket, message);
            });
            socket.on('announce_web_client', function (message: string) {
                rserver.log.info('Registering new web_client');
                rserver.announce_web_client(socket);
                socket.on('run', function (msg: any) {
                    stat.clear();
                    msg.agent = __.size(rserver.nodes);
                    console.log('server begin notify client to run machine...');
                    rserver.runconfig = msg;
                    let i = 0;
                    __.each(rserver.nodes, function (ele) {
                        // console.log(i++);
                        msg.index = i++;
                        ele.socket.emit('run', msg);
                    });
                    // rserver.io.sockets.in('nodes').emit('run',msg);
                    rserver.status = STATUS_RUNNING;
                    return;
                });
                socket.on('ready', function (msg: any) {
                    console.log('server begin ready client ...');
                    rserver.io.sockets.in('nodes').emit('disconnect', {});
                    stat.clear();
                    rserver.status = STATUS_READY;
                    rserver.runconfig = msg;
                    starter.run(rserver.conf.mainFile, msg, rserver.conf.clients);
                    return;
                });

                socket.on('exit4reready', function () {
                    __.each(rserver.nodes, function (obj: { socket:any}) {
                        obj.socket.emit('exit4reready');
                    });
                    rserver.nodes = {};
                    return;
                });

            });
        });

        // Broadcast heartbeat to all clients
        setInterval(function () {
            rserver.io.sockets.emit('heartbeat');
        }, HEARTBEAT_INTERVAL);
    }
}