user/tasks.js

'use strict';

const _tasks = require('../task/helper.js');
const _lists = require('../list/helper.js');
const taskIds = require('../utils/taskIds.js');
const errors = require('../response/error.js');

/**
 * This module returns the RTM Tasks-related functions for the RTMUser
 * @param {RTMUser} user The RTM User instance
 * @returns {{get: function, add:function, remove: function, complete: function, uncomplete: function, addTags: function, removeTags: function, priority: function, decreasePriority: function, increasePriority: function, move: function, setDueDate: function, postpone: function, setName: function}}
 * @private
 */
module.exports = function(user) {
  let rtn = {};

  /**
   * Get the list of RTM Tasks for this User.
   * @param {string} [filter] Tasks Filter (RTM Advanced Search Syntax)
   * @param {function} callback Callback function(err, tasks)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @param {RTMTask[]} callback.tasks List of User's RTM Tasks
   * @function RTMUser~tasks/get
   */
  rtn.get = function(filter, callback) {
    if ( callback === undefined && typeof filter === 'function' ) {
      callback = filter;
      filter = "";
    }

    // Callback counters
    let count = 0;
    let calls = 2;
    let returned = false;

    // Lists and Tasks
    let LISTS = {};
    let TASKS = [];

    // Update the User's Lists and Tasks
    _lists.get(user, function(err, lists) {
      if ( lists ) {
        for ( let i = 0; i < lists.length; i++ ) {
          LISTS[lists[i].id] = lists[i];
        }
      }
      _tasksUpdateCallback(err);
    });
    _tasks.get(user, filter, function(err, tasks) {
      TASKS = tasks;
      _tasksUpdateCallback(err);
    });

    // Callback for each of list and task updates
    function _tasksUpdateCallback(err) {
      count++;
      if ( !returned ) {
        if ( err ) {
          returned = true;
          return callback(err);
        }
        else if ( count === calls ) {
          returned = true;
          _parseTasks();
          return callback(null, TASKS);
        }
      }
    }

    // Add the List to each Task
    function _parseTasks() {
      for ( let i = 0; i < TASKS.length; i++ ) {
        let list = LISTS[TASKS[i].list_id];
        if ( list === undefined ) {
          list = {
            id: TASKS[i].list_id,
            name: "List #" + TASKS[i].list_id
          }
        }
        TASKS[i]._list = list;
      }
    }
  };

  /**
   * Get the RTMTask specified by its index
   * @param {int} index Task Index
   * @param {function} callback Callback function(err, task)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @param {RTMTask} callback.task Matching RTM Task
   * @function RTMUser~tasks/getTask
   */
  rtn.getTask = function(index, callback) {

    // Get Task Info
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Get Task From API
      user.tasks.get(function(err, tasks) {
        if ( err ) {
          return callback(err);
        }

        // Find Matching Task
        let found = false;
        for ( let i = 0; i < tasks.length; i++ ) {
          if ( !found && tasks[i].task_id === taskId ) {
            found = true;
            return callback(null, tasks[i]);
          }
        }

        // Task Not Found
        if ( !found ) {
          return callback(errors.referenceError());
        }

      });

    });

  };

  /**
   * Add a new RTM Task for this User
   * @param {string} name Task Name (can include 'Smart Add' syntax)
   * @param {object} [props] Task Properties
   * @param {string} props.due Task Due Date (RTM supported date format)
   * @param {int} props.priority Task Priority (1, 2, 3)
   * @param {string} props.list Task List Name
   * @param {string[]} props.tags Task Tags
   * @param {string} props.location Task Location Name
   * @param {string} props.start Task Start Date (RTM supported date format)
   * @param {string} props.repeat Task Repeat Format (RTM supported repeat format)
   * @param {string} props.estimate Task Time Estimate (RTM supported time estimate format)
   * @param {string} props.to Contact Name to give Task to (existing contact or email address)
   * @param {string} props.url Task Reference URL
   * @param {string} props.note Task Note
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/add
   */
  rtn.add = function(name, props, callback) {
    if ( callback === undefined && typeof props === 'function' ) {
      callback = props;
      props = {};
    }
    return _tasks.add(name, props, user, callback);
  };

  /**
   * Mark the specified Task as complete
   * @param {int} index Task Index
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/complete
   */
  rtn.complete = function(index, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Complete the Task
      return _tasks.complete(
        listId,
        taskSeriesId,
        taskId,
        user,
        callback
      );

    });

  };

  /**
   * Mark the specified Task as NOT complete
   * @param {int} index Task Index
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/uncomplete
   */
  rtn.uncomplete = function(index, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Complete the Task
      return _tasks.uncomplete(
        listId,
        taskSeriesId,
        taskId,
        user,
        callback
      );

    });

  };

  /**
   * Set the priority of the specified Task
   * @param {int} index Task Index
   * @param {int} priority Task Priority
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/priority
   */
  rtn.priority = function(index, priority, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Set the Priority
      return _tasks.priority(
        listId,
        taskSeriesId,
        taskId,
        priority,
        user,
        callback
      );

    });

  };

  /**
   * Add the specified tag(s) to the Task
   * @param {int} index Task Index
   * @param {string|string[]} tags Tag(s) to add to task
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/addTags
   */
  rtn.addTags = function(index, tags, callback) {

    // Make sure tags is an array
    if ( !Array.isArray(tags) ) {
      tags = [tags];
    }

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Add the Tags
      return _tasks.addTags(
        listId,
        taskSeriesId,
        taskId,
        tags,
        user,
        callback
      );

    });

  };


  /**
   * Add the specified note to the Task
   * @param {int} index Task Index
   * @param {string} title Title of the Note
   * @param {string|string[]} notes Note(s) to add to task
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/addNotes
   */
  rtn.addNotes = function(index, title, notes, callback) {
    var title = (typeof title !== 'undefined') ? title : "";

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Add the Note
      return _tasks.addNotes(
        listId,
        taskSeriesId,
        taskId,
    	title,
        notes,
        user,
        callback
      );

    });

  };

  /**
   * Remove the specified Task from the User's Account
   * @param {int} index Task Index
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/remove
   */
  rtn.remove = function(index, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Remove the Task
      return _tasks.remove(
        listId,
        taskSeriesId,
        taskId,
        user,
        callback
      );

    });

  };

  /**
   * Increase the Priority of the specified Task
   * @param {int} index Task Index
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/increasePriority
   */
  rtn.increasePriority = function(index, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Increase the Priority of the Task
      return _tasks.movePriority(
        listId,
        taskSeriesId,
        taskId,
        'up',
        user,
        callback
      );

    });

  };

  /**
   * Decrease the Priority of the specified Task
   * @param {int} index Task Index
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/decreasePriority
   */
  rtn.decreasePriority = function(index, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Decrease the Priority of the Task
      return _tasks.movePriority(
        listId,
        taskSeriesId,
        taskId,
        'down',
        user,
        callback
      );

    });

  };

  /**
   * Move the specified Task to a different List
   * @param {int} index Task Index
   * @param {string} listName List Name to move Task to
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/move
   */
  rtn.move = function(index, listName, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Get the List
      user.lists.get(function(err, lists) {
        if ( err ) {
          return callback(err);
        }

        // Find the List
        let id = [];
        for ( let i = 0; i < lists.length; i++ ) {
          if ( lists[i].name.toLowerCase() === listName.toLowerCase() ) {
            id.push(lists[i].id);
          }
        }

        // No List Match
        if ( id.length !== 1 ) {
          return callback(errors.referenceError());
        }

        // Move the Task
        else {
          return _tasks.move(
            listId,
            taskSeriesId,
            taskId,
            id[0],
            user,
            callback
          );
        }

      });

    });

  };

  /**
   * Postpone the due date of the Task by 1 day
   * @param {int} index Task Index
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/postpone
   */
  rtn.postpone = function(index, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Postpone the Task
      return _tasks.postpone(
        listId,
        taskSeriesId,
        taskId,
        user,
        callback
      );

    });

  };

  /**
   * Remove the specified tag(s) from the Task
   * @param {int} index Task Index
   * @param {string|string[]} tags Tags to remove from the Task
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/removeTags
   */
  rtn.removeTags = function(index, tags, callback) {

    // Make sure tags is an array
    if ( !Array.isArray(tags) ) {
      tags = [tags];
    }

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Remove the Tags
      return _tasks.removeTags(
        listId,
        taskSeriesId,
        taskId,
        tags,
        user,
        callback
      );

    });

  };

  /**
   * Set the Due Date of the specified Task
   * @param {int} index Task Index
   * @param {string} due The Due Date of the Task (RTM parsed date)
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/setDueDate
   */
  rtn.setDueDate = function(index, due, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Decrease the Priority of the Task
      return _tasks.setDueDate(
        listId,
        taskSeriesId,
        taskId,
        due,
        user,
        callback
      );

    });

  };

  /**
   * Set the Name of the specified Task
   * @param {int} index Task Index
   * @param {string} name New Task Name
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/setName
   */
  rtn.setName = function(index, name, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Decrease the Priority of the Task
      return _tasks.setName(
        listId,
        taskSeriesId,
        taskId,
        name,
        user,
        callback
      );

    });

  };

  /**
   * Set the URL of the specified Task
   * @param {int} index Task Index
   * @param {string} url New Task URL
   * @param {function} callback Callback function(err)
   * @param {RTMError} callback.err RTM API Error Response, if encountered
   * @function RTMUser~tasks/setURL
   */
  rtn.setURL = function(index, url, callback) {

    // Get the Task
    _getTaskInfo(index, function(err, listId, taskSeriesId, taskId) {
      if ( err ) {
        return callback(err);
      }

      // Decrease the Priority of the Task
      return _tasks.setURL(
        listId,
        taskSeriesId,
        taskId,
        url,
        user,
        callback
      );

    });

  };



  /**
   * Get the Matching Task IDs by Index
   * @param {int} index Task Index
   * @param callback Callback function(err, list_id, taskseries_id, task_id)
   * @private
   */
  function _getTaskInfo(index, callback) {

    // Get IDs
    let listId = taskIds.getListId(user.id, index);
    let taskSeriesId = taskIds.getTaskSeriesId(user.id, index);
    let taskId = taskIds.getTaskId(user.id, index);

    // All IDs found...
    if ( listId !== undefined && taskSeriesId !== undefined && taskId !== undefined ) {
      return callback(null, listId, taskSeriesId, taskId);
    }

    // Get Task From API
    user.tasks.get(function(err, tasks) {
      if ( err ) {
        return callback(err);
      }

      // Find Matching Task
      let found = false;
      for ( let i = 0; i < tasks.length; i++ ) {
        if ( !found && tasks[i].task_id === taskId ) {
          found = true;
          return callback(null, tasks[i].list_id, tasks[i].taskseries_id, tasks[i].task_id);
        }
      }

      // Task Not Found
      if ( !found ) {
        return callback(errors.referenceError());
      }
    });
  }

  return rtn;
};