'use strict';

var path = require('path'),
    regex = /\$X\.[\w\.\[\]]*/g, // find properties of $X
    listRules = {},
    listActionModules = {},
    isRunning = true,
    dynmod = require('./dynamic-modules'),
    db = require('./persistence'), log;

exports = module.exports = function( args ) {
  log = args.logger;
  db( args);
  dynmod(args);
  pollQueue();
  return module.exports;
};

var updateActionModules = function() {
  for ( var user in listRules ) {
    if(!listActionModules[user]) listActionModules[user] = {};
    for ( var rule in listRules[user] ) {
      var actions = listRules[user][rule].actions;
      console.log(actions);
      for ( var module in actions ){
        for ( var i = 0; i < actions[module]['functions'].length; i++ ){
          db.actionInvokers.getModule(module, function( err, objAM ){
            db.actionInvokers.getUserParams(module, user, function( err, objParams ) {
              console.log (objAM);

              //FIXME am name is called 'actions'???
              // if(objParams) { //TODO we don't need them for all modules
                var answ = dynmod.compileString(objAM.code, objAM.actions + "_" + user, objParams, objAM.lang);
                console.log('answ');
                console.log(answ);
                listActionModules[user][module] = answ.module;
                console.log('loaded ' + user + ': ' + module);
                console.log(listActionModules);
              // }
            });
          });
        }
      }
    }
  }
};

exports.internalEvent = function( evt ) {
  try {
    // TODO do we need to determine init from newRule?
    console.log (evt);
    // console.log (data);
    // var obj = JSON.parse( data );
    // db.getRuleActivatedUsers(obj.id, function ( err, arrUsers ) {
    //   console.log (arrUsers);
    //   for(var i = 0; i < arrUsers.length; i++) {
    //     if( !listRules[arrUsers[i]]) listRules[arrUsers[i]] = {};
    //     listRules[arrUsers[i]][obj.id] = obj;
    //     updateActionModules();
    //   }
    // });
  } catch( err ) {
    console.log( err );
  }
  console.log('internal event handled');
};


function pollQueue() {
  if(isRunning) {
    db.popEvent(function (err, obj) {
      if(!err && obj) {
        processEvent(obj);
      }
      setTimeout(pollQueue, 50); //TODO adapt to load
    });
  }
}

/**
 * Handles correctly posted events
 * @param {Object} evt The event object
 */
function processEvent(evt) {
  log.info('EN', 'processing event: ' + evt.event + '(' + evt.eventid + ')');
  var actions = checkEvent(evt);
  console.log('found actions to invoke:');
  console.log(actions);
  for(var user in actions) {
    for(var module in actions[user]) {
      for(var i = 0; i < actions[user][module]['functions'].length; i++) {
        var act = {
          module: module,
          function: actions[user][module]['functions'][i]
        }
        invokeAction(evt, user, act);
      }
    }
  }
}

/**
 FIXME merge with processEvent

 * Check an event against the rules repository and return the actions
 * if the conditons are met.
 * @param {Object} evt the event to check
 */
function checkEvent(evt) {
  var actions = {}, tEvt;
  for(var user in listRules) {
    actions[user] = {};
    for(var rule in listRules[user]) {
    //TODO this needs to get depth safe, not only data but eventually also
    // on one level above (eventid and other meta)
      tEvt = listRules[user][rule].event;
      if(tEvt.module + ' -> ' + tEvt.function === evt.event && validConditions(evt.payload, listRules[user][rule])) {
        log.info('EN', 'Rule "' + rule + '" fired');
        var oAct = listRules[user][rule].actions;
        console.log (oAct);
        for(var module in oAct) {
          if(!actions[user][module]) {
            actions[user][module] = {
              functions: []
            };
          }
          for(var i = 0; i < oAct[module]['functions'].length; i++ ){
            console.log ('processing action ' + i + ', ' + oAct[module]['functions'][i]);
            actions[user][module]['functions'].push(oAct[module]['functions'][i]);
            // if(actions[user].indexOf(arrAct[i]) === -1) actions[user].push(arrAct[i]);
          }
        }
      }
    }
  }
  return actions;
}

// {
//   "event": "emailyak -> newMail",
//   "payload": {
//     "TextBody": "hello"
//   }
// }
  
// exports.sendMail = ( args ) ->
//   url = 'https://api.emailyak.com/v1/ps1g59ndfcwg10w/json/send/email/'

//   data =
//     FromAddress: 'tester@mscliveweb.simpleyak.com'
//     ToAddress: 'dominic.bosch.db@gmail.com'
//     TextBody: 'test'

//   needle.post url, JSON.stringify( data ), {json: true}, ( err, resp, body ) ->
//     log err
//     log body
/**
 * Checks whether all conditions of the rule are met by the event.
 * @param {Object} evt the event to check
 * @param {Object} rule the rule with its conditions
 */
function validConditions(evt, rule) {
  for(var property in rule.conditions){
    if(!evt[property] || evt[property] != rule.condition[property]) return false;
  }
  return true;
}

/**
 * Invoke an action according to its type.
 * @param {Object} evt The event that invoked the action
 * @param {Object} action The action to be invoked
 */
function invokeAction( evt, user, action ) {
  console.log('invoking action');
  var actionargs = {};
      //FIXME internal events, such as loopback ha sno arrow
      //TODO this requires change. the module property will be the identifier
      // in the actions object (or shall we allow several times the same action?)
  console.log(action.module);
  console.log(listActionModules);
  var srvc = listActionModules[user][action.module];
  console.log(srvc);
  if(srvc && srvc[action.function]) {
    //FIXME preprocessing not only on data
    //FIXME no preprocessing at all, why don't we just pass the whole event to the action?'
    // preprocessActionArguments(evt.payload, action.arguments, actionargs);
    try {
      if(srvc[action.function]) srvc[action.function](evt.payload);
    } catch(err) {
      log.error('EN', 'during action execution: ' + err);
    }
  }
  else log.info('EN', 'No api interface found for: ' + action.module);
}

// /**
//  * Action properties may contain event properties which need to be resolved beforehand.
//  * @param {Object} evt The event whose property values can be used in the rules action
//  * @param {Object} act The rules action arguments
//  * @param {Object} res The object to be used to enter the new properties
//  */
// function preprocessActionArguments(evt, act, res) {
//   for(var prop in act) {
//     /*
//      * If the property is an object itself we go into recursion
//      */
//     if(typeof act[prop] === 'object') {
//       res[prop] = {};
//       preprocessActionArguments(evt, act[prop], res[prop]);
//     }
//     else {
//       var txt = act[prop];
//       var arr = txt.match(regex);
      
//        * If rules action property holds event properties we resolve them and
//        * replace the original action property
       
//       // console.log(evt);
//       if(arr) {
//         for(var i = 0; i < arr.length; i++) {
//           /*
//            * The first three characters are '$X.', followed by the property
//            */
//           var actionProp = arr[i].substring(3).toLowerCase();
//           // console.log(actionProp);
//           for(var eprop in evt) {
//             // our rules language doesn't care about upper or lower case
//             if(eprop.toLowerCase() === actionProp) {
//               txt = txt.replace(arr[i], evt[eprop]);
//             }
//           }
//           txt = txt.replace(arr[i], '[property not available]');
//         }
//       }
//       res[prop] = txt;
//     }
//   }
// }

exports.shutDown = function() {
  if(log) log.info('EN', 'Shutting down Poller and DB Link');
  isRunning = false;
  if(db) db.shutDown();
};
