<?php

// Internal status flags (for bitwise operations)
/** @constant TASK_ANY Task has any flag set. */
define('TASK_ANY', 0);

/** @constant TASK_ADDED Task has just been added. */
define('TASK_ADDED', 1);

/** @constant TASK_MODIFIED Task has been modified. */
define('TASK_MODIFIED', 2);

/** @constant TASK_DELETED Task has been deleted. */
define('TASK_DELETED', 4);

/**
 * Nag_Driver:: defines an API for implementing storage backends for Nag.
 *
 * $Horde: nag/lib/Driver.php,v 1.36 2003/03/18 20:48:59 chuck Exp $
 *
 * @author  Jon Parise <jon@horde.org>
 * @version $Revision: 1.36 $
 * @since   Nag 0.1
 * @package nag
 */
class Nag_Driver {

    /**
     * Array holding the current task list.  Each array entry is a hash
     * describing a task.  The array is indexed numerically by task ID.
     * @var array $tasks
     */
    var $_tasks = array();

    /**
     * String containing the current username.  This indicates the owner of
     * the current task list.
     * @var string $user
     */
    var $_user = '';

    /**
     * Attempts to return a concrete Nag_Driver instance based on $driver.
     *
     * @param string    $driver     The type of concrete Nag_Driver subclass
     *                              to return.  The is based on the storage
     *                              driver ($driver).  The code is dynamically
     *                              included.
     *
     * @param string    $user       The name of the user who owns these tasks.
     *
     * @param array     $params     (optional) A hash containing any additional
     *                              configuration or connection parameters a
     *                              subclass might need.
     *
     * @return mixed    The newly created concrete Nag_Driver instance, or
     *                  false on an error.
     */
    function &factory($driver, $user, $params = array())
    {
        $driver = basename($driver);
        require_once dirname(__FILE__) . '/Driver/' . $driver . '.php';
        $class = 'Nag_Driver_' . $driver;
        if (class_exists($class)) {
            return new $class($user, $params);
        } else {
            return false;
        }
    }

    /**
     * Attempts to return a reference to a concrete Nag_Driver
     * instance based on $driver. It will only create a new instance
     * if no Nag_Driver instance with the same parameters currently
     * exists.
     *
     * This should be used if multiple storage sources are required.
     *
     * This method must be invoked as: $var = &Nag_Driver::singleton()
     *
     * @param string    $driver     The type of concrete Nag_Driver subclass
     *                              to return.  The is based on the storage
     *                              driver ($driver).  The code is dynamically
     *                              included.
     *
     * @param string    $user       The name of the user who owns these tasks.
     *
     * @param array     $params     (optional) A hash containing any additional
     *                              configuration or connection parameters a
     *                              subclass might need.
     *
     * @return mixed    The created concrete Nag_Driver instance, or false
     *                  on error.
     */
    function &singleton($driver, $user, $params = array())
    {
        static $instances;

        if (!isset($instances)) {
            $instances = array();
        }

        $signature = serialize(array($driver, $user, $params));
        if (!isset($instances[$signature])) {
            $instances[$signature] = &Nag_Driver::factory($driver, $user, $params);
        }

        return $instances[$signature];
    }

    /**
     * Lists tasks based on the given criteria.  All tasks will be returned
     * by default.
     *
     * @param optional constant $criteria  A set of flags specifying the
     *                                     set of tasks to return.
     *
     * @return array    Returns a list of the requested tasks.
     */
    function listTasks($criteria = TASK_ANY)
    {
        /* Return all of the tasks by default. */
        if ($criteria == TASK_ANY) {
            return $this->_tasks;
        }

        /* Otherwise, return only the set of tasks that match $criteria. */
        $return_set = array();
        foreach ($this->_tasks as $task_id => $task) {
            if ($this->_tasks[$task_id]['flags'] & $criteria) {
                $return_set[$task_id] = $task;
            }
        }

        return $return_set;
    }

    /**
     * Adds a new task to the $tasks list.
     *
     * @param string           $name      The name (short) of the task.
     * @param string           $desc      The description (long) of the task.
     * @param optional integer $due       The due date of the task.
     * @param optional integer $priority  The priority of the task.
     * @param optional integer $completed The completion state of the task.
     * @param optional integer $category  The category of the task.
     * @param optional integer $completed The alarm associatesd to the task.
     *
     * @return integer  The numeric ID of the new task.
     */
    function addTask($name, $desc, $due = 0, $priority = 0, $completed = 0,
                     $category = 0, $alarm = 0)
    {
        /* Create a new task task. */
        $task = array();
        $task['name'] = $name;
        $task['desc'] = $desc;
        $task['added'] = time();
        $task['due'] = $due;
        $task['priority'] = $priority;
        $task['completed'] = $completed;
        $task['category'] = $category;
        $task['alarm'] = $alarm;
        $task['flags'] = TASK_ADDED;

        /* Add it to the $tasks list. */
        $this->_tasks[] = $task;

        $keys = array_keys($this->_tasks);

        return $keys[(count($keys) - 1)];
    }

    /**
     * Modifies an attribute of a task in the $tasks list.
     *
     * @param int       $id         The ID of the task to modify.
     * @param string    $attribute  The attribute of the task to modify.
     * @param mixed     $value      The new value for $attribute.
     *
     * @return boolean  True if the task was successfully marked as modified.
     */
    function modifyTask($id, $attribute, $value)
    {
        if (isset($this->_tasks[$id]) && isset($this->_tasks[$id][String::lower($attribute)])) {
            $this->_tasks[$id][String::lower($attribute)] = $value;
            $this->_tasks[$id]['flags'] |= TASK_MODIFIED;
            return ($this->_tasks[$id]['flags'] & TASK_MODIFIED);
        }

        return false;
    }

    /**
     * Removes a task from the $tasks list.
     *
     * @param int       $id         The ID of the task to delete.
     *
     * @return boolean  True if the task was successfully marked as deleted.
     */
    function deleteTask($id)
    {
        if (array_key_exists($id, $this->_tasks)) {
            $this->_tasks[$id]['flags'] |= TASK_DELETED;
            return ($this->_tasks[$id]['flags'] & TASK_DELETED);
        }

        return false;
    }

    /**
     * Sets the requested flag to the given value.
     *
     * @param int       $id         The ID of the task task to modify.
     * @param int       $flag       The flag to modify.
     * @param boolean   $state      The new state of the flag.
     */
    function setFlag($id, $flag, $state)
    {
        if (isset($this->_tasks[$id])) {
            if ($state) {
                $this->_tasks[$id]['flags'] |= $flag;
            } else {
                $this->_tasks[$id]['flags'] &= ~$flag;
            }
        }
    }

    /**
     * Gets the requested flag's value.
     *
     * @param int       $id         The ID of the task task to examine.
     * @param int       $flag       The flag whose value to return.
     *
     * @return boolean  The state of the requested flag.
     */
    function getFlag($id, $flag)
    {
        if (isset($this->_tasks[$id])) {
            return ($this->_tasks[$id]['flags'] & $flag);
        }

        return null;
    }

    /**
     * Purges those tasks that have the "deleted" flag set.
     */
    function purgeDeleted()
    {
        $deleted_ids = array_keys($this->listTasks(TASK_DELETED));
        foreach ($deleted_ids as $task_id) {
            unset($this->_tasks[$task_id]);
        }
    }

    /**
     * Delete all tasks from the list
     *
     * @since Nag 1.1
     */
    function deleteAll()
    {
        foreach ($this->_tasks as $id => $task) {
            $this->_tasks[$id]['flags'] |= TASK_DELETED;
        }
    }

}
