'use strict';
/*global events, module, require, __plugin, setInterval, clearInterval, setTimeout, addUnloadHandler*/
var utils = require('utils');
/************************************************************************
## The at Module

The at module provides a single function `at()` which can be used to schedule
repeating (or non-repeating) tasks to be done at a particular time. 

### at() function

The utils.at() function will perform a given task at a given time in the 
(minecraft) day.

#### Parameters

 * time24hr : The time in 24hr form - e.g. 9:30 in the morning is '09:30' while
   9:30 pm is '21:30', midnight is '00:00' and midday is '12:00'
 * callback : A javascript function which will be invoked at the given time.
 * worlds : (optional) An array of worlds. Each world has its own clock. If no array of worlds is specified, all the server's worlds are used.
 * repeat : (optional) true or false, default is true (repeat the task every day)

#### Example

To warn players when night is approaching:

```javascript
var utils = require('utils'),
    at = require('at');
function warning(){
  utils.players(function( player ) {
    echo( player, 'The night is dark and full of terrors!' );
  });
}
at('19:00', warning);
```
To run a task only once at the next given time:
```javascript
var utils = require('utils'),
    at = require('at');
function wakeup(){
  utils.players(function( player ) {
    echo( player, "Wake Up Folks!" );
  });
}
at('06:00', wakeup, null, false);
```

***/
var SECOND = 1000;
var POLLING_INTERVAL = 3 * SECOND; // this is probably precise enough 

function at(time24hr, callback, pWorlds, repeat) {
  if (arguments.length === 0){
    // TODO: Document this behaviour
    console.log(tasksToString());
    return;
  }
  var timeParts = time24hr.split( ':' );
  var timeMins = (timeParts[0] * 60) + (timeParts[1] * 1);
  if (!pWorlds ||  pWorlds === undefined ) {
    pWorlds = utils.worlds();
  }
  if (repeat === undefined){
    repeat = true;
  }
  utils.foreach( pWorlds, function ( world ) {
    atAddTask( timeMins, callback, world, repeat);
  });
};
var atTasks = {};

function tasksToString(){
  var result = '';
  for (var world in atTasks){
    result += 'world: ' + world +'\n';
    for (var time in atTasks[world]){
      var scheduledFuncs = atTasks[world][time];
      for (var i = 0;i < scheduledFuncs.length; i++){
	result += ' ' + time + ': ' + scheduledFuncs[i].constructor + '\n';
      }
    }
    result += '(current world time: ' + utils.time24(world) + ')\n';
  }
  return result;
}
/*
 constructs a function which will be called every x ticks to 
 track the schedule for a given world
*/
function atMonitorFactory(world){
  var worldName = ''+ world.name;
  var lastRun = null;

  return function atMonitorForWorld(){
    var timeMins = utils.time24(world);
    if (timeMins === lastRun){
      return;
    }
    if (lastRun === null ){
      lastRun = timeMins - 1;
    }else {
      lastRun = lastRun % 1440;
    }
    var worldSchedule = atTasks[worldName];
    if (!worldSchedule){
      return;
    }
    while ( lastRun > timeMins ? (lastRun <= 1440) : ( lastRun < timeMins ) ){

      var tasks = worldSchedule[lastRun++];
      if (!tasks){
	continue;
      }
      utils.foreach(tasks, function(task, i){
	if (!task){
	  return;
	}
	setTimeout(task.callback.bind(null, timeMins, world), 1);
	if (!task.repeat){
	  tasks[i] = null;
	}
      });
    }
  };
}
function atAddTask( timeMins, callback, world, repeat){
  var worldName = ''+world.name;
  if (!atTasks[worldName]){
    atTasks[worldName] = {};
  }
  if (!atTasks[worldName][timeMins]){
    atTasks[worldName][timeMins] = [];
  }
  atTasks[worldName][timeMins].push({callback: callback, repeat: repeat});
}
var atMonitors = [];
function onLoadStartMonitor(event){
  var monitor = setInterval( atMonitorFactory(event.world), POLLING_INTERVAL);
  atMonitors.push( monitor );
}
if (__plugin.canary){
  events.loadWorld( onLoadStartMonitor );
}
if (__plugin.bukkit){
  events.worldLoad( onLoadStartMonitor );
}

addUnloadHandler(function(){
  utils.foreach(atMonitors, function(atInterval){
    clearInterval(atInterval);
  });
});

module.exports = at;
