<?php

require_once 'Horde/Kolab.php';

/**
 * Horde Nag driver for the Kolab IMAP server.
 *
 * $Horde: nag/lib/Driver/kolab.php,v 1.23 2007/01/11 17:13:49 chuck Exp $
 *
 * Copyright 2004-2007 The Horde Project (http://www.horde.org/)
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Stuart Binge <omicron@mighty.co.za>
 * @package Nag
 */
class Nag_Driver_kolab extends Nag_Driver {

    /**
     * Our Kolab server connection.
     *
     * @var Kolab
     */
    var $_kolab = null;

    /**
     * Shortcut to the imap connection
     *
     * @var Kolab_IMAP
     */
    var $_store = null;

    /**
     * Constructs a new Kolab storage object.
     *
     * @param string $tasklist  The tasklist to load.
     * @param array $params     A hash containing connection parameters.
     */
    function Nag_Driver_kolab($tasklist, $params = array())
    {
        $this->_tasklist = $tasklist;
        $this->_params = $params;
    }

    /**
     * Attempts to open a Kolab Groupware folder.
     *
     * @return boolean  True on success, PEAR_Error on failure.
     */
    function initialize()
    {
        if (isset($this->_kolab)) {
            return true;
        }

        $this->_kolab = new Kolab();
        if (empty($this->_kolab->version)) {
            Horde::logMessage('The Framework Kolab package must be upgraded', __FILE__, __LINE__, PEAR_LOG_ERR);
            return PEAR::raiseError(_("Unable to access Kolab shares."));
        }
        $this->_store = $this->_kolab->_storage;

        return $this->_kolab->open($this->_tasklist);
    }

    /**
     * Disconnect from the Kolab backend
     */
    function _disconnect()
    {
        $this->_kolab->close();
        $this->_kolab = null;
    }

    /**
     * Split the tasklist name of the id. We use this to make ids
     * unique across folders.
     *
     * @param string $id The ID of the note prepended with the tasklist
     *                   name.
     *
     * @return string  The note id.
     */
    function _splitId($id)
    {
        return substr($id, strlen($this->_tasklist) + 1);
    }

    /**
     * Prepend the tasklist name to the id. We use this to make ids
     * unique across folders.
     *
     * @param string $id The ID of the note
     *
     * @return string  The note id prepended with the tasklist
     *                 name.
     */
    function _uniqueId($id)
    {
        return $this->_tasklist . ':' . $id;
    }

    /**
     * Retrieves one task from the store.
     *
     * @param string $taskId  The id of the task to retrieve.
     *
     * @return array  The array of task attributes.
     */
    function get($taskId)
    {
        $taskId = $this->_splitId($taskId);

        $result = $this->initialize();
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        if ($this->_store->objectUidExists($taskId)) {
            $task = $this->_store->getObject($taskId);
            return $this->_buildTask($task);
        } else {
            return PEAR::raiseError(sprintf(_('Turba/kolab: Did not find task %s'), $taskId));
        }
    }

    /**
     * Retrieves 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)
    {
        return PEAR::raiseError("Not supported");
    }

    /**
     * Add or modify a task.
     *
     * @todo Utilize $owner, $assignee, and $completed_date
     * parameters.
     *
     * @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 float $estimate          The estimated time to complete the task.
     * @param integer $completed       The completion state of the task.
     * @param string $category         The category of the task.
     * @param integer $alarm           The alarm associated with the task.
     * @param string $uid              A Unique Identifier for the task.
     * @param boolean $private         Whether the task is private.
     * @param string $owner            The owner of the event.
     * @param string $assignee         The assignee of the event.
     * @param integer $completed_date  The task's completion date.
     *
     * @return mixed The id of the task if successful, a PEAR error
     * otherwise
     */
    function _setObject($name, $desc, $due = 0, $priority = 0, $estimate = 0.0,
                        $completed = 0, $category = 0, $alarm = 0,
                        $private = false, $owner = null, $assignee = null,
                        $completed_date = null)
    {
        $result = $this->initialize();
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        if (empty($uid)) {
            $task_uid = $this->_store->generateUID();
        } else {
            $task_uid = $uid;
        }

        if ($completed) {
            $completed = 100;
        } else {
            $completed = 0;
        }
        
        if ($private) {
            $private = 'private';
        } else {
            $private = 'public';
        }
        
        if (isset($task['organizer']) && isset($task['organizer']['smtp-address'])) {
            $task['assignee'] = $task['organizer']['smtp-address'];
        }

        $result = $this->_store->save(array(
                                          'uid' => $task_uid,
                                          'summary' => $name,
                                          'body' => $desc,
                                          'due-date' => $due,
                                          'priority' => $priority,
                                          'completed' => $completed,
                                          'categories' => $category,
                                          'alarm' => $alarm,
                                          'private' => $private,
                                          'organizer' => array(
                                              'smpt-address' => $assignee
                                          ),
                                      ), 
                                      $uid);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return $task_uid;
    }

    /**
     * Adds 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 float $estimate     The estimated time to complete the task.
     * @param integer $completed  The completion state of the task.
     * @param string $category    The category of the task.
     * @param integer $alarm      The alarm associated with the task.
     * @param string $uid         A Unique Identifier for the task.
     * @param boolean $private    Whether the task is private.
     * @param string $owner       The owner of the event.
     * @param string $assignee    The assignee of the event.
     *
     * @return mixed The id of the task if successful, a PEAR error
     * otherwise
     */
    function _add($name, $desc, $due = 0, $priority = 0, $estimate = 0.0,
                  $completed = 0, $category = '', $alarm = 0, $uid = null,
                  $private = false, $owner = null, $assignee = null)
    {
        return $this->_setObject($name, $desc, $due, $priority, $estimate,
                                 $completed, $category, $alarm, null,
                                 $private, $owner, $assignee);
    }

    /**
     * Modifies an existing task.
     *
     * @param string $taskId           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 float $estimate          The estimated time to complete the task.
     * @param integer $completed       The completion state of the task.
     * @param string $category         The category of the task.
     * @param integer $alarm           The alarm associated with the task.
     * @param boolean $private         Whether the task is private.
     * @param string $owner            The owner of the event.
     * @param string $assignee         The assignee of the event.
     * @param integer $completed_date  The task's completion date.
     *
     * @return mixed The id of the task if successful, a PEAR error
     * otherwise
     */
    function _modify($taskId, $name, $desc, $due = 0, $priority = 0,
                     $estimate = 0.0, $completed = 0, $category = 0,
                     $alarm = 0, $private = false, $owner = null,
                     $assignee = null, $completed_date = null)
    {
        $taskId = $this->_splitId($taskId);

        return $this->_setObject($name, $desc, $due, $priority, $estimate,
                                 $completed, $category, $alarm, $taskId,
                                 $private, $owner, $assignee);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return $result == $taskId;
    }

    /**
     * Moves a task to a different tasklist.
     *
     * @param string $taskId       The task to move.
     * @param string $newTasklist  The new tasklist.
     *
     * @return mixed  True on success, PEAR_Error on failure.
     */
    function move($taskId, $newTasklist)
    {
        $taskId = $this->_splitId($taskId);

        $result = $this->initialize();
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return $this->_store->move($taskId, $newTasklist);
    }

    /**
     * Deletes a task from the backend.
     *
     * @param string $taskId  The task to delete.
     */
    function _delete($taskId)
    {
        $taskId = $this->_splitId($taskId);

        $result = $this->initialize();
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return $this->_store->delete($taskId);
    }

    /**
     * Deletes all tasks from the backend.
     */
    function deleteAll()
    {
        $result = $this->initialize();
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return $this->_store->deleteAll();
    }

    /**
     * Retrieves tasks from the Kolab server.
     *
     * @param integer $completed  Which tasks to retrieve (1 = all tasks,
     *                            0 = incomplete tasks, 2 = complete tasks).
     *
     * @return mixed  True on success, PEAR_Error on failure.
     */
    function retrieve($completed = 1)
    {
        $result = $this->initialize();
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        $this->_tasks = array();

        $task_list = $this->_store->getObjects();
        if (is_a($task_list, 'PEAR_Error')) {
            return $task_list;
        }

        if (empty($task_list)) {
            return true;
        }

        foreach ($task_list as $task) {
            $tuid = $this->_uniqueId($task['uid']);
            $this->_tasks[$tuid] = $this->_buildTask($task);
        }

        return true;
    }

    /**
     * Build a task based a data array
     *
     * @param array  $object     The data for the task
     *
     * @return array  The converted data array representing the task
     */
    function _buildTask($object)
    {
        $task = array();

        $task['tasklist_id'] = $this->_tasklist;
        $task['task_id'] = $this->_uniqueId($object['uid']);
        $task['name'] = $object['summary'];
        $task['desc'] = $object['body'];
        if (isset($object['due-date'])) {
            $task['due'] = $object['due-date'];
        }else {
            $task['due'] = null;
        }
        $task['priority'] = $object['priority'];
        $task['completed'] = Kolab::percentageToBoolean($object['completed']);
        if (isset($object['categories'])) {
            $task['category'] = $object['categories'];
        } else {
            $task['category'] = '';
        }
        if (isset($object['due-date'])) {
            $task['alarm'] = $object['alarm'];
        } else {
            $task['alarm'] = null;
        }
        if ($object['sensitivity'] == 'public') {
            $task['private'] = false;
        } else {
            $task['private'] = true;
        }
        $task['estimate'] = null;
        $share = $GLOBALS['nag_shares']->getShare($this->_tasklist);
        $task['owner'] = $share->get('owner');
        if (isset($task['organizer']) && isset($task['organizer']['smtp-address'])) {
            $task['assignee'] = $task['organizer']['smtp-address'];
        }

        return $task;
    }

    /**
     * Lists 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_list = $this->_store->getObjects();
        if (is_a($task_list, 'PEAR_Error')) {
            return $task_list;
        }

        if (empty($task_list)) {
            return array();
        }

        $tasks = array();
        foreach ($task_list as $task) {
            $tuid = $this->_uniqueId($task['uid']);
            $t = $this->_buildTask($task);
            if ($t['alarm'] && $t['due'] && 
                $t['due'] - $t['alarm'] * 60 < $date) {
                $tasks[] = $t;
            }
        }

        return $tasks;
    }



}