<?php

require_once 'Horde/DataTree.php';

/**
 * Nag storage implementation for the Horde DataTree driver.
 *
 * $Horde: nag/lib/Driver/datatree.php,v 1.14.2.4 2005/07/01 01:36:44 selsky Exp $
 *
 * @author  Jay 'Eraserhead' Felice <jfelice@cronosys.com>
 * @since   Nag 3.0
 * @package Nag
 */
class Nag_Driver_datatree extends Nag_Driver {

    /**
     * Reference to the DataTree instance.
     *
     * @var DataTree
     */
    var $_datatree;

    /**
     * Constructs a new SQL storage object.
     *
     * @param string $tasklist  The tasklist to load.
     * @param array  $params    A hash containing connection parameters.
     */
    function Nag_Driver_datatree($tasklist, $params = array())
    {
        global $conf;

        $this->_tasklist = $tasklist;
        $this->_params = $params;

        $driver = $conf['datatree']['driver'];
        $params = Horde::getDriverConfig('datatree', $driver);
        $params = array_merge($params, array('group' => 'nag.tasks'));

        $this->_datatree = &DataTree::singleton($driver, $params);
    }

    /**
     * Retrieve one task from the database.
     *
     * @param integer $taskId  The id of the task to retrieve.
     *
     * @return array  The array of task attributes.
     */
    function get($taskId)
    {
        $obj = $this->_datatree->getObjectById($taskId);
        return array(
            'tasklist_id' => $this->_tasklist,
            'task_id'     => $taskId,
            'uid'         => $obj->get('uid'),
            'name'        => $obj->get('name'),
            'desc'        => $obj->get('description'),
            'category'    => $obj->get('category'),
            'due'         => $obj->get('due'),
            'priority'    => $obj->get('priority'),
            'completed'   => $obj->get('completed'),
            'alarm'       => $obj->get('alarm'),
            'flags'       => 0,
        );
    }

    /**
     * Retrieve one task from the database by UID.
     *
     * @param string $uid  The UID of the task to retrieve.
     *
     * @return array  The array of task attributes.
     */
    function getByUID($uid)
    {
        // Needs to be written to use datatree attributes to do a
        // search.
        return PEAR::raiseError('DataTree driver needs a getByUID() method. Needs to use DataTree Attributes for storage, too.');
    }

    /**
     * Retrieves tasks from the DataTree driver.
     *
     * @return mixed  True on success, PEAR_Error on failure.
     */
    function retrieve()
    {
        $this->_tasks = array();

        $task_ids = $this->_datatree->get(DATATREE_FORMAT_FLAT);
        foreach ($task_ids as $task_id => $name) {
            if ($task_id == DATATREE_ROOT) {
                continue;
            }
            $obj = $this->_datatree->getObjectById($task_id);
            if ($obj->get('tasklist') == $this->_tasklist) {
                $this->_tasks[$task_id] = array(
                    'task_id'     => $task_id,
                    'tasklist_id' => $this->_tasklist,
                    'uid'         => $obj->get('uid'),
                    'name'        => $obj->get('name'),
                    'desc'        => $obj->get('description'),
                    'category'    => $obj->get('category'),
                    'due'         => $obj->get('due'),
                    'priority'    => $obj->get('priority'),
                    'completed'   => $obj->get('completed'),
                    'alarm'       => $obj->get('alarm'),
                    'flags'       => 0,
                    );
            }
        }

        return true;
    }

    /**
     * Add a task to the backend storage.
     *
     * @param string $name        The name (short) of the task.
     * @param string $desc        The description (long) of the task.
     * @param integer $due        The due date of the task.
     * @param integer $priority   The priority of the task.
     * @param integer $completed  The completion state of the task.
     * @param string $category    The category of the task.
     * @param integer $completed  The alarm associated with the task.
     * @param string $uid         A Unique Identifier for the task.
     *
     * @return string  The Nag ID of the new task.
     */
    function add($name, $desc, $due = 0, $priority = 0, $completed = 0,
                 $category = '', $alarm = 0, $uid = null)
    {
        if (is_null($uid)) {
            $uid = $this->generateUID();
        }

        // Create a new DataTree object.
        $obj = &new DataTreeObject(md5(uniqid(mt_rand(), true)));
        $obj->set('tasklist', $this->_tasklist);
        $obj->set('name', $name);
        $obj->set('uid', $uid);
        $obj->set('description', $desc);
        $obj->set('due', $due);
        $obj->set('priority', $priority);
        $obj->set('completed', $completed);
        $obj->set('category', $category);
        $obj->set('modified', time());
        $obj->set('alarm', $alarm);
        $result = $this->_datatree->add($obj);

        /* Return an error immediately if adding failed. */
        if (is_a($result, 'PEAR_Error')) {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $result;
        }

        /* Log the creation of this item in the history log. */
        $history = &Horde_History::singleton();
        $history->log($uid, array('action' => 'add'), true);

        /* Result contains the ID sent back by the add function. */
        return $result;
    }

    /**
     * Modify an existing task.
     *
     * @param integer $task_id    The task to modify.
     * @param string $name        The name (short) of the task.
     * @param string $desc        The description (long) of the task.
     * @param integer $due        The due date of the task.
     * @param integer $priority   The priority of the task.
     * @param integer $completed  The completion state of the task.
     * @param string $category    The category of the task.
     * @param integer $completed  The alarm associated with the task.
     */
    function modify($task_id, $name, $desc, $due = 0, $priority = 0,
                    $completed = 0, $category = '', $alarm = 0)
    {
        /* Retrieve the task object from the backend. */
        $obj = $this->_datatree->getObjectById($task_id);
        $obj->set('description', $desc);
        $obj->set('tasklist', $this->_tasklist);
        $obj->set('due', $due);
        $obj->set('priority', $priority);
        $obj->set('completed', $completed);
        $obj->set('category', $category);
        $obj->set('modified', time());
        $obj->set('alarm', $alarm);
        $result = $this->_datatree->updateData($obj);

        /* Return immediately if there was an error. */
        if (is_a($result, 'PEAR_Error')) {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $result;
        }

        /* Log the modification of this item in the history log. */
        $history = &Horde_History::singleton();
        $history->log($obj->get('uid'), array('action' => 'modify'), true);

        return $result;
    }

    /**
     * Move a task to a different tasklist.
     *
     * @param string $taskId     The task to move.
     * @param string $newTaskId  The new tasklist.
     */
    function move($taskId, $newTaskList)
    {
        $obj = $this->_datatree->getObjectById($taskId);
        $obj->set('tasklist', $newTaskList);
        $obj->set('modified', time());
        $result = $this->_datatree->updateData($obj);

        if (is_a($result, 'PEAR_Error')) {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $result;
        }

        $history = &Horde_History::singleton();
        $history->log($obj->get('uid'), array('action' => 'move'), true);

        return $result;
    }

    /**
     * Delete an existing task from the backend.
     *
     * @param integer task_id    ID of the task to delete.
     *
     * @return mixed    true if success, PEAR_Error if failure.
     */
    function delete($task_id)
    {
        $obj = $this->_datatree->getObjectById($task_id);
        $ret = $this->_datatree->remove($obj);

        if (is_a($ret, 'PEAR_Error')) {
            Horde::logMessage($ret, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $ret;
        }

        /* Log the deletion of this item in the history log. */
        $history = &Horde_History::singleton();
        $history->log($obj->get('uid'), array('action' => 'delete'), true);

        return true;
    }

    /**
     * Purge a task list.
     *
     * @return mixed    true if success, PEAR_Error if failure.
     */
    function deleteAll()
    {
        /* Retrieve all the task for the task list. */
        $task_ids = $this->_datatree->get(DATATREE_FORMAT_FLAT);
        foreach ($task_ids as $task_id => $name) {
            if ($task_id == DATATREE_ROOT) {
                continue;
            }
            if (($result = $this->delete($task_id)) != true) {
                break;
            }
        }

        return is_a($result, 'PEAR_Error') ? $result : true;
    }

    /**
     * List all alarms near $date.
     *
     * @param integer $date  The unix epoch time to check for alarms.
     *
     * @return array  An array of tasks that have alarms that match.
     */
    function listAlarms($date)
    {
        $tasks = array();

        $task_ids = $this->_datatree->get(DATATREE_FORMAT_FLAT);
        foreach ($task_ids as $task_id => $name) {
            if ($task_id == DATATREE_ROOT) {
                continue;
            }
            $obj = $this->_datatree->getObjectById($task_id);

            // Check the alarm.
            if ($obj->get('alarm') > 0
                && ($obj->get('due') - $obj->get('alarm') * 60) <= $date
                && $obj->get('due') >= time()) {
                $tasks[$task_id] = array(
                    'task_id'     => $task_id,
                    'tasklist_id' => $this->_tasklist,
                    'name'        => $obj->get('name'),
                    'desc'        => $obj->get('description'),
                    'category'    => $obj->get('category'),
                    'due'         => $obj->get('due'),
                    'priority'    => $obj->get('priority'),
                    'completed'   => $obj->get('completed'),
                    'alarm'       => $obj->get('alarm'),
                    'flags'       => 0,
                );
            }
        }

        return $tasks;
    }

}
