var cluster = require('cluster');

if (cluster.isMaster) {
  const isWinOS = process.platform == 'win32';
  const instances = (process.argv[2] != null && process.argv[2] != '' && !isNaN(process.argv[2]) && Number.isInteger(parseFloat(process.argv[2])) ? parseInt(process.argv[2]) : require('os').cpus().length);

  console.log('Starting cluster with pid: ' + process.pid);

  function startWorkers() {
    // create worker instances
    for (var i = 0; i < instances; i += 1)
      cluster.fork();
  }

  function killAllWorkers(isRestart) {
    // send kill cmd to all workers
    for (var id in cluster.workers) {
      console.log('Worker (' + id + ') shutting down...');

      if (!isWinOS || isRestart) {
        // only kill the worker if not on windows (otherwise an error is displayed) or if the cluster is being restarted
        process.kill(cluster.workers[id]['process']['pid'], 'SIGINT');
      }
    }
  }

  function waitForWorkerShutdown(isRestart) {
    if (Object.keys(cluster.workers).length > 0) {
      // continue waiting since worker threads are still open
      setTimeout(waitForWorkerShutdown, 100, isRestart);
    } else {
      // all worker threads have closed
      // check if the cluster process should stop or if the workers should be restarted
      if (isRestart) {
        // start new worker threads
        startWorkers();
      } else {
        // exit the master process
        
        if (isWinOS) {
          // command line in windows doesn't seem to release itself
          // show a message that the app has finished
          console.log('The application is now safe to close');
        }

        process.exit(0);
      }
    }
  }

  // ensure workers exit cleanly
  process.on('SIGINT', () => {
    console.log('Cluster shutting down..');

    // send kill cmd to all workers
    killAllWorkers(false);

    // wait for workers to stop
    waitForWorkerShutdown(false);
  });

  startWorkers();

  // listen for dying workers
  cluster.on('exit', function (worker, code, signal) {
    if (worker['process']['exitCode'] === 0) {
      console.log('Worker (' + worker['id'] + ') shutdown complete');
    } else if (signal != 'SIGINT' && worker['process']['exitCode'] !== 0 && worker['process']['exitCode'] !== 999 && worker.exitedAfterDisconnect !== true) {
      console.log('Cluster restarting...');
      cluster.fork();
    }
  });

  // listen for the restart cmd
  cluster.on('message', function (data, msg) {
      // check if this was the restart cmd
      if (msg != null && msg == 'restart') {
        console.log('Cluster restarting..');

        // send kill cmd to all workers
        killAllWorkers(true);

        // wait for workers to stop
        waitForWorkerShutdown(true);
      }
  });
} else
  require('./instance');