<?php

/** @const MOMENT_FB_URL Name of free busy url field in addressbook. */
define('MOMENT_FB_URL', 'freebusyUrl');

// Actions
/** @const MOMENT_NEW Start a new meeting             */ define('MOMENT_NEW', 101);
/** @const MOMENT_RESET Clear the current meeting     */ define('MOMENT_RESET', 102);
/** @const MOMENT_CANCEL Cancel meeting creation      */ define('MOMENT_CANCEL', 103);
/** @const MOMENT_CANCEL_MEETING Cancel a meeting     */ define('MOMENT_CANCEL_MEETING', 104);
/** @const MOMENT_DELEVE_MEETING Delete a meeting     */ define('MOMENT_DELETE_MEETING', 105);
/** @const MOMENT_SAVE_MEETING Save a meeting         */ define('MOMENT_SAVE_MEETING', 106);
/** @const MOMENT_ADD_ATTENDEE Add an attendee        */ define('MOMENT_ADD_ATTENDEE', 107);
/** @const MOMENT_DELETE_ATTENDEE Delete an attedee   */ define('MOMENT_DELETE_ATTENDEE', 108);
/** @const MOMENT_SEND Send and invite or update      */ define('MOMENT_SEND', 109);
/** @const MOMENT_SEND_REQUEST Send vFreebusy request */ define('MOMENT_SEND_REQUEST', 110);

// Sort types
/** @const MOMENT_SORT_TITLE Sort by title.              */ define('MOMENT_SORT_TITLE', 0);
/** @const MOMENT_SORT_LOCATION Sort by location.        */ define('MOMENT_SORT_LOCATION', 1);
/** @const MOMENT_SORT_START Sort by start date.         */ define('MOMENT_SORT_START', 2);
/** @const MOMENT_SORT_STATUS Sort by meeting status.    */ define('MOMENT_SORT_STATUS', 3);

/** @const MOMENT_SORT_ASCEND Sort in ascending order.   */ define('MOMENT_SORT_ASCEND', 0);
/** @const MOMENT_SORT_DESCEND Sort in descending order. */ define('MOMENT_SORT_DESCEND', 1);

/**
 * $Horde: moment/lib/Moment.php,v 1.28 2003/09/01 22:56:37 jan Exp $
 *
 * Copyright 2003 Mike Cochrane <mike@graftonhall.co.nz>
 *
 * See the enclosed file LICENSE for license information.
 */
class Moment {

    function listMeetings($sortby = MOMENT_SORT_START,
                          $sortdir = MOMENT_SORT_ASCEND)
    {
        global $prefs, $conf;

        /* Sorting criteria for the task list. */
        $sort_functions = array(
            MOMENT_SORT_TITLE    => 'ByTitle',
            MOMENT_SORT_LOCATION => 'ByLocation',
            MOMENT_SORT_START    => 'ByStart',
            MOMENT_SORT_STATUS   => 'ByStatus',
        );

        /* Create a storage instance. */
        $storage = &Moment_Driver::singleton();
        if (is_a($storage, 'PEAR_Error')) {
            return $storage;
        }
        $storage->retrieve();

        /* Retrieve the meetings for storage. */
        $meetings = $storage->listMeetings();

        /* Filter complete/incomplete meetings if the user doesn't
         * want them shown.*/
        $time = time();
        if ($prefs->getValue('show_completed') == 0) {
            // Incomplete meetings
            foreach ($meetings as $meeting_id => $meeting) {
                if ($meeting['end'] <= $time || $meeting['status'] == 'cancelled') {
                    unset($meetings[$meeting_id]);
                }
            }
        } elseif ($prefs->getValue('show_completed') == 2) {
            // Complete meetings
            foreach ($meetings as $meeting_id => $meeting) {
                if ($meeting['end'] > $time && $meeting['status'] != 'cancelled') {
                    unset($meetings[$meeting_id]);
                }
            }
        }

        // Sort the array if we have a sort function defined for this
        // field.
        if (array_key_exists($sortby, $sort_functions)) {
            $prefix = ($sortdir == MOMENT_SORT_DESCEND) ? '_rsort' : '_sort';
            uasort($meetings, array('Moment', $prefix . $sort_functions[$sortby]));
        }

        return $meetings;
    }

    /**
     * Check if free/busy information is going to be available for
     * a given email address.
     *
     * @param $email    Email address to look for
     *
     * @return boolean  True on free/busy information available
     */
    function freebusyAvailable($email)
    {
        if (Moment::getFreeBusyUrl($email)) {
            // TODO: check the url is available and valid vfreebusy at the url.
            return true;
        }

        // Check storage driver
        global $conf;
        require_once MOMENT_BASE . '/lib/Storage.php';
        $storage = &Moment_Storage::singleton();

        $res = $storage->search($email);
        if (!is_a($res, 'PEAR_Error')) {
            return true;
        }

        return false;
    }

    /**
     * Retrieve the free/busy information for a given email address.
     *
     * @param $email    Email address to look for
     *
     * @return Horde_iCalendar_vfreebusy Freebusy object.
     */
    function getFreeBusy($email)
    {
        global $prefs;

        // Make sure a cache location exists
        if (!array_key_exists('moment', $_SESSION)) {
            $_SESSION['moment'] = array();
        }
        if (!array_key_exists('fb_cache', $_SESSION['moment'])) {
            $_SESSION['moment']['fb_cache'] = array();
        }
        $cache = &$_SESSION['moment']['fb_cache'];

        // Check addressbook for free busy url and request.
        if (!array_key_exists($email, $cache) || !is_array($cache[$email]) ||
            $cache[$email]['last_refreshed'] <
            time() - $prefs->getValue('cache_time')) {

            $url = Moment::getFreeBusyUrl($email);

            if ($url !== false) {
                $lines = file($url);

                if ($lines !== false) {
                    $data = implode('', $lines);
                    $vCal = &new Horde_iCalendar();
                    $vCal->parsevCalendar($data);

                    $freebusy = false;
                    foreach ($vCal->getComponents() as $component) {
                        if ($freebusy === false) {
                            $freebusy = $component;
                        } else {
                            $freebusy->merge($component);
                        }
                    }
                    $cache[$email]['last_refreshed'] = time();
                    $cache[$email]['fb'] = $freebusy;
                    return $cache[$email]['fb'];
                }
            }

            // Check storage driver
            global $conf;
            require_once MOMENT_BASE . '/lib/Storage.php';
            $storage = &Moment_Storage::singleton();

            $res = $storage->search($email);
            if (!is_a($res, 'PEAR_Error')) {
                $cache[$email]['last_refreshed'] = time();
                $cache[$email]['fb'] = $res;
                return $cache[$email]['fb'];
            }

            $vCal = &new Horde_iCalendar();
            $vFb = &Horde_iCalendar::newComponent('vfreebusy', $vCal);
            $vFb->setAttribute('ORGANIZER', $email);

            $cache[$email]['last_refreshed'] = time();
            $cache[$email]['fb'] = $vFb;
        }

        return $cache[$email]['fb'];
    }

    /**
     * Search the contacts sources for the freebusy url
     * for a given email address.
     *
     * @params String   $email  The email address to look for.
     *
     * @return Mixed    (string) The url on success
     *                  (boolean) False on failure
     */
    function getFreeBusyUrl($email)
    {
        global $registry;

        /* Get the lists of address books through API */
        $source_list = $registry->call('contacts/sources');
        if (is_a($source_list, 'PEAR_Error')) {
            return false;
        }

        /* Try retrieving by e-mail only first. */
        $result = $registry->call('contacts/getField', array($email, MOMENT_FB_URL, array_keys($source_list)));

        if (is_a($result, 'PEAR_Error')) {
            return false;
        }
        return $result;
    }

    /**
     * Create an iCal free busy request
     *
     * @return Horde_iCalendar The Request.
     */
    function iCalFreeBusyRequest($meeting)
    {
        require_once HORDE_BASE . '/lib/Identity.php';

        // Get the Identity for the organizer.
        $identity = &new Identity(Auth::getAuth());
        $email = $identity->getValue('from_addr');
        $cn = $identity->getValue('fullname');

        // Create new iCalendar
        require_once MOMENT_BASE . '/lib/version.php';
        $vCal = &new Horde_iCalendar();
        $vCal->setAttribute('PRODID', '-//The Horde Project//Moment ' . MOMENT_VERSION . '//EN');
        $vCal->setAttribute('METHOD', 'REQUEST');

        // Create new vFreebusy
        $vFb = &Horde_iCalendar::newComponent('vfreebusy', $vCal);
        $vFb->setAttribute('DTSTAMP', time());
        $vFb->setAttribute('DTSTART', $meeting['start']);
        $vFb->setAttribute('DTEND', $meeting['end']);
        $vFb->setAttribute('UID', $meeting['uid']);

        // Add Organizer
        $params = array();
        if (!empty($cn)) {
            $params['CN'] = $cn;
        }
        if (empty($email)) {
            $email = $meeting['organizer'];
        }
        $vFb->setAttribute('ORGANIZER', 'MAILTO:' . $email, $params);

        $params = array('ROLE' => 'CHAIR', 'PARTSTAT' => 'ACCEPTED');
        if (!empty($cn)) {
            $params['CN'] = $cn;
        }
        $vFb->setAttribute('ATTENDEE', 'MAILTO:' . $email, $params);

        // Add Attendees
        foreach ($meeting['attendees'] as $attendee) {
            $params = array('CUTYPE' => 'INDIVIDUAL');
            if ($attendee['attendance'] == 'required') {
                $params['ROLE'] = 'REQ-PARTICIPANT';
            } else {
                $params['ROLE'] = 'OPT-PARTICIPANT';
            }
            if ($attendee['responce'] == 'none') {
                $params['RSVP'] = 'TRUE';
                $params['PARTSTAT'] = 'NEEDS-ACTION';
            } else {
                $params['PARTSTAT'] = String::upper($attendee['responce']);
            }
            $vFb->setAttribute('ATTENDEE', 'MAILTO:' . $attendee['email'], $params);
        }

        $vCal->addComponent($vFb);

        return $vCal;
    }

    /**
     * Create a iCal request for the given meeting.
     * This can be used to send updates or RSVP requests.
     *
     * @params array $meeting The meeting.
     *
     * @return Horde_iCalendar The Request.
     */
    function iCalRequest($meeting)
    {
        require_once HORDE_BASE . '/lib/Identity.php';

        // Get the Identity for the organizer.
        $identity = &new Identity(Auth::getAuth());
        $email = $identity->getValue('from_addr');
        $cn = $identity->getValue('fullname');

        // Create new iCalendar
        require_once MOMENT_BASE . '/lib/version.php';
        $vCal = &new Horde_iCalendar();
        $vCal->setAttribute('PRODID', '-//The Horde Project//Moment ' . MOMENT_VERSION . '//EN');
        $vCal->setAttribute('METHOD', 'REQUEST');

        // Create new vEvent.
        $vEvent = &Horde_iCalendar::newComponent('vevent', $vCal);
        $vEvent->setAttribute('DTSTAMP', time());
        $vEvent->setAttribute('DTSTART', $meeting['start']);
        $vEvent->setAttribute('DTEND', $meeting['end']);
        $vEvent->setAttribute('UID', $meeting['uid']);

        $params = array();
        if (!empty($cn)) {
            $params['CN'] = $cn;
        }
        if (!empty($email)) {
            $email = $meeting['organizer'];
        }
        $vEvent->setAttribute('ORGANIZER', 'MAILTO:' . $email, $params);

        $vEvent->setAttribute('SUMMARY', $meeting['title']);
        $vEvent->setAttribute('DESCRIPTION', $meeting['description']);
        $vEvent->setAttribute('LOCATION', $meeting['location']);
        $vEvent->setAttribute('SEQUENCE', $meeting['sequence']);

        $params = array('ROLE' => 'CHAIR', 'PARTSTAT' => 'ACCEPTED');
        if (!empty($cn)) {
            $params['CN'] = $cn;
        }
        $vEvent->setAttribute('ATTENDEE', 'MAILTO:' . $email, $params);

        foreach ($meeting['attendees'] as $attendee) {
            $params = array('CUTYPE' => 'INDIVIDUAL');
            if ($attendee['attendance'] == 'required') {
                $params['ROLE'] = 'REQ-PARTICIPANT';
            } else {
                $params['ROLE'] = 'OPT-PARTICIPANT';
            }
            if ($attendee['responce'] == 'none') {
                $params['RSVP'] = 'TRUE';
                $params['PARTSTAT'] = 'NEEDS-ACTION';
            } else {
                $params['PARTSTAT'] = String::upper($attendee['responce']);
            }
            $vEvent->setAttribute('ATTENDEE', 'MAILTO:' . $attendee['email'], $params);
        }

        $vCal->addComponent($vEvent);

        return $vCal;
    }

    /**
     * Create a iCal cancellation notificaton for the given meeting.
     *
     * @params array $meeting The meeting.
     *
     * @return Horde_iCalendar The Cancellation.
     */
    function iCalCancel($meeting)
    {
        require_once HORDE_BASE . '/lib/Identity.php';

        // Get the Identity for the organizer.
        $identity = &new Identity(Auth::getAuth());
        $email = $identity->getValue('from_addr');
        $cn = $identity->getValue('fullname');

        // Create new iCalendar
        require_once MOMENT_BASE . '/lib/version.php';
        $vCal = &new Horde_iCalendar();
        $vCal->setAttribute('PRODID', '-//The Horde Project//Moment ' . MOMENT_VERSION . '//EN');
        $vCal->setAttribute('METHOD', 'CANCEL');

        // Create new vEvent.
        $vEvent = &Horde_iCalendar::newComponent('vevent', $vCal);
        $vEvent->setAttribute('DTSTAMP', time());
        $vEvent->setAttribute('DTSTART', $meeting['start']);
        $vEvent->setAttribute('DTEND', $meeting['end']);
        $vEvent->setAttribute('UID', $meeting['uid']);

        $params = array();
        if (!empty($cn)) {
            $params['CN'] = $cn;
        }
        if (!empty($email)) {
            $email = $meeting['organizer'];
        }
        $vEvent->setAttribute('ORGANIZER', 'MAILTO:' . $email, $params);

        $vEvent->setAttribute('SUMMARY', $meeting['title']);
        $vEvent->setAttribute('DESCRIPTION', $meeting['description']);
        $vEvent->setAttribute('LOCATION', $meeting['location']);
        $vEvent->setAttribute('SEQUENCE', $meeting['sequence']);
        $vEvent->setAttribute('STATUS', 'CANCELLED');

        $params = array('ROLE' => 'CHAIR', 'PARTSTAT' => 'ACCEPTED');
        if (!empty($cn)) {
            $params['CN'] = $cn;
        }
        $vEvent->setAttribute('ATTENDEE', 'MAILTO:' . $email, $params);

        foreach ($meeting['attendees'] as $attendee) {
            $params = array('CUTYPE' => 'INDIVIDUAL');
            if ($attendee['attendance'] == 'required') {
                $params['ROLE'] = 'REQ-PARTICIPANT';
            } else {
                $params['ROLE'] = 'OPT-PARTICIPANT';
            }
            if ($attendee['responce'] == 'none') {
                $params['RSVP'] = 'TRUE';
                $params['PARTSTAT'] = 'NEEDS-ACTION';
            } else {
                $params['PARTSTAT'] = String::upper($attendee['responce']);
            }
            $vEvent->setAttribute('ATTENDEE', 'MAILTO:' . $attendee['email'], $params);
        }

        $vCal->addComponent($vEvent);

        return $vCal;
    }

    /**
     * Transtale a meeting status as stored to a translated
     * string.
     *
     * @param string $status    The status of the meeting
     *
     * @return string   The translated meeting status
     */
    function statusToString($status)
    {
        switch ($status) {
            case 'cancelled':
                return _("Cancelled");

            case 'confirmed':
                return _("Confirmed");

            case 'tentative':
            default:
                return _("Tentative");
        }
    }

    /**
     * Comparison function for sorting meetings by title.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _sortByTitle($a, $b)
    {
        return strcmp($a['title'], $b['title']);
    }

    /**
     * Comparison function for reverse sorting meetings by title.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _rsortByTitle($a, $b)
    {
        return strcmp($b['title'], $a['title']);
    }

    /**
     * Comparison function for sorting meetings by location.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _sortByLocation($a, $b)
    {
        return strcmp($a['location'], $b['location']);
    }

    /**
     * Comparison function for reverse sorting meetings by location.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _rsortByLocation($a, $b)
    {
        return strcmp($b['location'], $a['location']);
    }

    /**
     * Comparison function for sorting meetings by start.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _sortByStart($a, $b)
    {
        if ($a['start'] == $b['start']) return 0;
        return ($a['start'] > $b['start']) ? -1 : 1;
    }

    /**
     * Comparison function for reverse sorting meetings by start.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _rsortByStart($a, $b)
    {
        if ($a['start'] == $b['start']) return 0;
        return ($b['start'] > $a['start']) ? -1 : 1;
    }

    /**
     * Comparison function for sorting meetings by status.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _sortByStatus($a, $b)
    {
        return strcmp($a['status'], $b['status']);
    }

    /**
     * Comparison function for reverse sorting meetings by status.
     *
     * @param array $a  Meeting one.
     * @param array $b  Meeting two.
     *
     * @return integer  1 if meeting one is greater, -1 if meeting two is greater;
     *                  0 if they are equal.
     */
    function _rsortByStatus($a, $b)
    {
        return strcmp($b['status'], $a['status']);
    }

    function menu()
    {
        global $notification, $conf, $registry;
        require_once HORDE_BASE . '/lib/Menu.php';
        require MOMENT_TEMPLATES . '/menu/menu.inc';

        $notification->notify();

        /* Include the JavaScript for the help system. */
        Help::javascript();
    }

}
