'use strict';

const ChainJobQueue = require('./chain-job-queue'),
  path = require('path'),
  bluebird = require('bluebird'),
  winston = require('winston'),
  fs = bluebird.promisifyAll(require('fs')),
  redis = require('redis'),
  clientRedis = redis.createClient(),
  cluster = require('cluster'),
  numberFork = require('os').cpus().length / 2;

const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.File)({
      filename: 'logs/sisyphe.log',
      level: 'info'
    })
  ]
});
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);

class Sisyphe {
  constructor(starter, workers) {
    const defaultStarter = {
      module: "walker-fs",
      options: {
        path: path.resolve(__dirname + "/../test/dataset")
      }
    };
    this.starter = starter || defaultStarter;
    const defaultWorkers = [{
      name: "Alpha",
      module: "alpha-worker"
    }, {
      name: "Beta",
      module: "beta-worker"
    }];
    this.workers = workers || defaultWorkers;
  }

  startToGenerateTask() {
    this.starterModule.start();
  }

  start() {
    this.initializeWorker().then(() => {
      if (cluster.isMaster) {
        for (var i = 0; i < numberFork; i++) {
          const fork = cluster.fork();
          fork.on('online', () => {
            logger.info('fork created');
          });
          fork.on('exit', () => {
            logger.info('fork exit');
          });
        }
        this.initializeStarter().then(() => this.startToGenerateTask());
      }
      this.activateWorker();
    });
  }

  initializeWorker() {
    const workerDirectory = path.resolve(__dirname + "/../worker");
    this.workflow = new ChainJobQueue();

    return bluebird.map(this.workers, (worker) => {
      return fs.accessAsync(workerDirectory + "/" + worker.module)
    }).then(() => {
      return this.workers.map((worker) => {
        const pathToWorkerModule = workerDirectory + "/" + worker.module;
        const workerModule = require(pathToWorkerModule);
        const packageWorkerModule = require(pathToWorkerModule + '/package.json');
        return {
          name: packageWorkerModule.name,
          doTheJob: workerModule.doTheJob,
          finalJob: workerModule.finalJob
        };
      })
    }).then((arrayWorkerModule) => {
      arrayWorkerModule.map((workerModule) => {
        this.workflow.addWorker(workerModule.name, workerModule.doTheJob, workerModule.finalJob);
      });
      this.workflow.createQueueForWorkers();
      return this;
    });
  }

  activateWorker() {
    const callFinishers = () => {
      return bluebird.filter(this.workflow.listWorker, (worker) => {
        return worker.finalFunction !== undefined
      }).map((worker) => {
        return bluebird.promisify(worker.finalFunction)();
      })
    };

    // Heartbeat
    if (cluster.isMaster) {
      setInterval(function () {
        clientRedis.hgetallAsync('sisyphe').then((values) => {
          const totalJobs = +[values.totalPerformedTask] + +[values.totalFailedTask];
          if (+values.totalGeneratedTask && totalJobs >= +values.totalGeneratedTask) {
            logger.info("Total jobs created = " + values.totalGeneratedTask);
            logger.info("Total jobs completed = " + values.totalPerformedTask);
            logger.info("Total jobs failed = " + values.totalFailedTask);
            logger.info("Total jobs = " + totalJobs);
            logger.info('release finishers !');
            callFinishers().then(() => {
              logger.info('All finalJob executed !');
              clientRedis.del('sisyphe');
              clearInterval(this);
              console.log('');
              console.log('This is the end !');
            }).catch((error) => {
              // TODO : rajouter une gestion des erreur pour les logs
              logger.info(error);
            });
          }
        });
      }, 1000);
    }

    this.workflow.addJobProcessToWorkers();
    return this;
  }

  initializeStarter() {
    const starterDirectory = path.resolve(__dirname + "/../starter");
    const pathToStarterModule = starterDirectory + "/" + this.starter.module;
    return fs.accessAsync(pathToStarterModule).then(() => {
      const StarterModule = require(starterDirectory + "/" + this.starter.module);
      this.starterModule = new StarterModule(this.starter.options);

      this.starterModule.setFunctionEventOnData((data) => {
        this.starterModule.totalFile++;
        this.workflow.addTask(data);
      });

      this.starterModule.setFunctionEventOnEnd(() => {
        this.workflow.totalGeneratedTask = this.starterModule.totalFile;
        clientRedis.hset('sisyphe', 'totalGeneratedTask', this.starterModule.totalFile);
        logger.info('Total jobs generated by starter module = ' + this.starterModule.totalFile);
      });

      return this;
    });
  }
}

module.exports = Sisyphe;
@@ -3136,24 +3136,57 @@
 atedTask) %7B%0A
+            clearInterval(this);%0A
             
@@ -3648,43 +3648,8 @@
 ');%0A
-              clearInterval(this);%0A
     

Kill earlier the heartbeat