<?php
/**
 * Kronolith_Event:: defines a generic API for events
 *
 * $Horde: kronolith/lib/Event.php,v 1.86 2003/08/07 03:40:22 chuck Exp $
 *
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @version $Revision: 1.86 $
 * @since   Kronolith 0.1
 * @package kronolith
 */
class Kronolith_Event {

    /**
     * Flag that is set to true if this event has been created in the
     * storage driver.
     * @var boolean $initialized
     */
    var $initialized = false;

    /**
     * The driver unique identifier for this event.
     * @var integer $eventID
     */
    var $eventID = null;

    /**
     * The user id of the creator of the event
     * @var string $creatorID
     */
    var $creatorID = null;

    /**
     * The title of this event,
     * @var string $title
     */
    var $title = '';

    /**
     * The identifier of the category this event belongs to.
     * @var integer $category
     */
    var $category = 0;

    /**
     * The location this event occurs at.
     * @var string $location
     */
    var $location = '';

    /**
     * The description for this event
     * @var string $description
     */
    var $description = '';

    /**
     * All the key words associtated with this event.
     * @var array $keywords
     */
    var $keywords = array();

    /**
     * All the exceptions for this event if this recuring.
     * @var array $exceptions
     */
    var $exceptions = array();

    /**
     * The timestamp for the start of this event.
     * @var integer $startTimestamp
     */
    var $startTimestamp = 0;

    /**
     * The timestamp for the end of this event.
     * @var integer $endTimestamp
     */
    var $endTimestamp = 0;

    /**
     * The duration of this event in minutes
     * @var integer $durMin
     */
    var $durMin = 0;

    /**
     * TODO what does this value represent?
     * @var integer $alarm
     */
    var $alarm = 0;

    /**
     * The timestamp for the end of the recurrance interval.
     * @var integer $recurEndTimestamp
     */
    var $recurEndTimestamp = null;

    /**
     * The type of recurrance this event follows. KRONOLITH_RECUR_* constant.
     * @var KRONOLITH_RECUR_* $recurType
     */
    var $recurType = KRONOLITH_RECUR_NONE;

    /**
     * TODO The length of time between recurances in seconds?
     * @var integer $recurInterval
     */
    var $recurInterval = null;

    /**
     * TODO
     * @var
     */
    var $recurData = null;

    /**
     * The identifier of the calender this event exists on.
     * @var string $_calendar
     */
    var $_calendar;

    /**
     * Constructor
     *
     * @param Kronolith_Driver $driver        The backend driver that this
     *                                        event is stored in.
     * @param Kronolith_Event  $eventObject   Backend specific event object
     *                                        that this will represent.
     */
    function Kronolith_Event(&$driver, $eventObject = null)
    {
        $this->_calendar = $driver->getCalendar();

        if (isset($eventObject)) {
            $this->fromDriver($eventObject);
        }
    }

    /**
     * Return a reference to a driver that's valid for this event.
     *
     * @return Kronolith_Driver  A driver that this event can use to save itself, etc.
     */
    function &getDriver()
    {
        global $calendar;
        if ($calendar->getCalendar() != $this->_calendar) {
            $calendar->close();
            $calendar->open($this->_calendar);
        }

        return $calendar;
    }

    /**
     * Create a new event.
     */
    function createEvent()
    {
        $this->toDriver();
        $this->initialized = true;
    }

    /**
     * Export this event in iCalendar format.
     *
     * param Identity $identity     The Identity object of the organizer.
     * param Horde_Data_iCalendar $vcs  (optional) The data object to use to
     *                                  format the dates.
     *
     * @return @array   Array of all the iCal attributes and their values.
     */
    function toiCalendar($identity, $vcs = null)
    {
        global $prefs;

        if (is_null($vcs)) {
            include_once HORDE_BASE . '/lib/Data.php';
            $vcs = &Horde_Data::singleton('icalendar');
        }

        $DTSTAMP = date("Ymd\TGis");
        $UTCTIME = date("Ymd\TGis", (time() + date('Z')));

        $vcal['DTSTAMP'] = $DTSTAMP;
        $vcal['UID'] = $this->getGUID();
        $vcal['SUMMARY'] = $this->title;
        $vcal['DESCRIPTION'] = $this->description;
        $vcal['DTSTART'] = $vcs->makeDate($this->start);
        $vcal['DTEND'] = $vcs->makeDate($this->end);
        $vcal['LOCATION'] = $this->location;
        $vcal['TRANSP'] = 'OPAQUE';
        $ORGANIZER = 'ORGANIZER;' . 'CN=' . $identity->getValue('fullname');
        $vcal[$ORGANIZER] = 'MAILTO:' . $identity->getValue('from_addr');

        return $vcal;
    }

    /**
     * Import the values for this event from an array of values,
     *
     * @param array $hash Array containing all the values.
     */
    function fromHash($hash)
    {
        // see if it's a new event
        if (is_null($this->getID())) {
            $this->setCreatorID(Auth::getAuth());
        }
        if (!empty($hash['title'])) {
            $this->setTitle($hash['title']);
        }
        if (!empty($hash['description'])) {
            $this->setDescription($hash['description']);
        }
        if (!empty($hash['category'])) {
            $this->setCategory($hash['category']);
        }
        if (!empty($hash['location'])) {
            $this->setLocation($hash['location']);
        }
        if (!empty($hash['keywords'])) {
            $this->setKeywords(explode(',', $hash['keywords']));
        }
        if (!empty($hash['start_date']) && !empty($hash['start_time'])) {
            $date = explode('-', $hash['start_date']);
            $time = explode(':', $hash['start_time']);
            if (count($time) == 2) {
                $time[2] = 0;
            }
            if (count($time) == 3 && count($date) == 3) {
                $this->setStartTimestamp(mktime($time[0], $time[1], $time[2], $date[1], $date[2], $date[0]));
            }
        }
        if (!empty($hash['end_date']) && !empty($hash['end_time'])) {
            $date = explode('-', $hash['end_date']);
            $time = explode(':', $hash['end_time']);
            if (count($time) == 2) {
                $time[2] = 0;
            }
            if (count($time) == 3 && count($date) == 3) {
                $this->setEndTimestamp(mktime($time[0], $time[1], $time[2], $date[1], $date[2], $date[0]));
            }
        }
        if (!empty($hash['alarm'])) {
            $this->setAlarm($hash['alarm']);
        } elseif (!empty($hash['alarm_date']) &&
                  !empty($hash['alarm_time'])) {
            $date = explode('-', $hash['alarm_date']);
            $time = explode(':', $hash['alarm_time']);
            if (count($time) == 2) {
                $time[2] = 0;
            }
            if (count($time) == 3 && count($date) == 3) {
                $this->setAlarm(($this->getStartTimestamp() - mktime($time[0], $time[1], $time[2], $date[1], $date[2], $date[0])) / 60);
            }
        }
        if (!empty($hash['recur_type'])) {
            $this->setRecurType($hash['recur_type']);
            if (!empty($hash['recur_end_date'])) {
                $date = explode('-', $hash['recur_end_date']);
                $this->setRecurEndTimestamp(mktime(0, 0, 0, $date[1], $date[2], $date[0]));
            }
            if (!empty($hash['recur_interval'])) {
                $this->setRecurInterval($hash['recur_interval']);
            }
            if (!empty($hash['recur_data'])) {
                $this->setRecurOnDay($hash['recur_data']);
            }
        }
    }

    /**
     * Save changes to this event
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function save()
    {
        if (!$this->initialized) {
            return false;
        }

        $this->toDriver();
        $driver = &$this->getDriver();
        return $driver->saveEvent($this);
    }

    /**
     * Add an exception to a recuring event.
     *
     * @param integer $year     The year of the execption.
     * @param integer $month    The month of the execption.
     * @param integer $mday     The day of the month of the execption.
     */
    function addException($year, $month, $mday)
    {
        $this->exceptions[] = sprintf("%04d%02d%02d", $year, $month, $mday);
    }

    /**
     * Check if an exception exists for a given recurance of an event
     *
     * @param integer $year     The year of the reucrance.
     * @param integer $month    The month of the reucrance.
     * @param integer $mday     The day of the month of the reucrance.
     *
     * @return boolean  True if an exception exists for the given date.
     */
    function hasException($year, $month, $mday)
    {
        return in_array(sprintf("%04d%02d%02d", $year, $month, $mday), $this->getExceptions());
    }

    /**
     * Retrieve all the exceptions for this event
     *
     * @return array    Array containing the dates of all the exceptions in
     *                  YYYYMMDD form.
     */
    function getExceptions()
    {
        return $this->exceptions;
    }

    /**
     * TODO
     */
    function isInitialized()
    {
        return $this->initialized;
    }

    /**
     * Check if this event recurs on a given day of the week.
     *
     * @param integer $dayMask  A mask specifying the day(s) to check.
     *
     * @return boolean  True if this event recurs on the given day(s).
     */
    function recurOnDay($dayMask)
    {
        return ($this->recurData & $dayMask);
    }

    /**
     * Specify the days this event recurs on
     *
     * @param integer $dayMask  A mask specifying the day(s) to recur on.
     */
    function setRecurOnDay($dayMask)
    {
        $this->recurData = $dayMask;
    }

    /**
     * Return the days this event recurs on.
     *
     * @return intenger A mask specifying the day(s) this event recurs on.
     */
    function getRecurOnDays()
    {
        return $this->recurData;
    }

    function getRecurType()
    {
        return $this->recurType;
    }

    function hasRecurType($recurrence)
    {
        return ($recurrence === $this->recurType);
    }

    function setRecurType($recurrence)
    {
        $this->recurType = $recurrence;
    }

    /**
     * Set the length of time between recurrances of this event.
     *
     * @param integer $interval     The number of seconds between recurances.
     */
    function setRecurInterval($interval)
    {
        $this->recurInterval = $interval;
    }

    /**
     * Retrieve the length of time between recurrances fo this event.
     *
     * @return integer  The number of seconds between recurances.
     */
    function getRecurInterval()
    {
        return $this->recurInterval;
    }

    /**
     * Retrieve the locally unique identifier for this event.
     *
     * @return integer  The local identifier for this event.
     */
    function getID()
    {
        return $this->eventID;
    }

    /**
     * Set the locally unique identifier for this event.
     *
     * @param integer $eventID  The local identifier for this event.
     */
    function setID($eventID)
    {
        $this->eventID = $eventID;
    }

    /**
     * Retrieve the id of the user who created the event 
     *
     * @return string The creator id 
     */
    function getCreatorID()
    {
        return !empty($this->creatorID) ? $this->creatorID : Auth::getAuth();
    }

    /**
     * Set the id of the creator of the event 
     *
     * @param string $creatorID The user id for who created the event 
     */
    function setCreatorID($creatorID)
    {
        $this->creatorID = $creatorID;
    }

    /**
     * Retrieve the globally unique identifier for this event.
     *
     * @return integer  The globally identifier for this event.
     */
    function getGUID()
    {
       return sprintf('%s-%s@%s', $this->getCalendar(), $this->getID(), $_SERVER["SERVER_NAME"]);
    }

    /**
     * Retrieve the title of this event.
     *
     * @return string  The title of this event.
     */
    function getTitle()
    {
        if (isset($this->taskID) || isset($this->remoteCal) || isset($this->meetingID)) {
            return !empty($this->title) ? $this->title : _("[none]");
        }

        if (!$this->isInitialized()) {
            return '';
        }

        $share = &$GLOBALS['shares']->getShare($this->getCalendar());
        if (!is_a($share, 'PEAR_Error') && $share->hasPermission(Auth::getAuth(), _PERMS_READ, $this->getCreatorID())) {
            return !empty($this->title) ? $this->title : _("[none]");
        } else {
            global $prefs;
            return sprintf(_("Event from %s to %s"),
                           $this->getStartDate($prefs->getValue('twentyFour') ? 'G:i' : 'g:ia'),
                           $this->getEndDate($prefs->getValue('twentyFour') ? 'G:i' : 'g:ia'));
        }
    }

    /**
     * Set the title of this event.
     *
     * @param string  The new title for this event.
     */
    function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * Retieve the description of this event.
     *
     * @return string The description of this event.
     */
    function getDescription()
    {
        return $this->description;
    }

    /**
     * Set the description of this event.
     *
     * @param string $description The new description for this event.
     */
    function setDescription($description)
    {
        $this->description = $description;
    }

    /**
     * Set the category this event belongs to.
     *
     * @param integer $category     The identifier of the category this
     *                                  event belongs to.
     */
    function setCategory($category)
    {
        $this->category = $category;
    }

    /**
     * Reteieve the category this event belongs to.
     *
     * @return integer The identifier of the category this event belongs to.
     */
    function getCategory()
    {
        return $this->category;
    }

    /**
     * Set the location this event occurs at.
     *
     * @param string $location  The new location for this event.
     */
    function setLocation($location)
    {
        $this->location = $location;
    }

    /**
     * Retrieve the location this event occurs at.
     *
     * @return string   The lcoation of this event.
     */
    function getLocation()
    {
        return $this->location;
    }

    /**
     * Retrieve the time this event starts.
     *
     * @param integer $dayTimestamp   (optional) The timestamp of a day a
     *                                      recurrance occurs. First recurrance
     *                                      used if not specified.
     *
     * @return integer The timestamp for the start of this event.
     */
    function getStartTimestamp($dayTimestamp = null)
    {
        if (!isset($dayTimestamp)) {
            return $this->startTimestamp;
        }

        list($year, $month, $day) = explode(':', date('Y:n:j', $dayTimestamp));
        list($hour, $min, $sec) = explode(':', date('G:i:s', $this->startTimestamp));

        return mktime($hour, $min, $sec, $month, $day, $year);
    }

    /**
     * Set the time this event starts.
     *
     * @param integer $startTimestamp   The timestamp for the start
     *                                      of this event.
     */
    function setStartTimestamp($startTimestamp)
    {
        $this->startTimestamp = $startTimestamp;
        $this->start = new stdClass();
        list($this->start->mday, $this->start->month, $this->start->year,
             $this->start->hour, $this->start->min, $this->start->sec) =
             explode(':', date('d:m:Y:H:i:s', $startTimestamp));
    }

    function setKeywords($keywords)
    {
        $this->keywords = $keywords;
    }

    function getKeywords()
    {
        return $this->keywords;
    }

    function hasKeyword($keyword)
    {
        return in_array($keyword, $this->keywords);
    }

    function getStartDate($formatString, $dayTimestamp = null)
    {
        return date($formatString, $this->getStartTimestamp($dayTimestamp));
    }

    function getStartDatestamp()
    {
        return mktime(0, 0, 0,
                      $this->getStartDate('n'),
                      $this->getStartDate('j'),
                      $this->getStartDate('Y'));
    }

    function setEndTimestamp($endTimestamp)
    {
        $this->endTimestamp = $endTimestamp;
        $this->end = new stdClass();
        list($this->end->mday, $this->end->month, $this->end->year,
             $this->end->hour, $this->end->min, $this->end->sec) =
             explode(':', date('d:m:Y:H:i:s', $endTimestamp));
    }

    function getEndTimestamp($dayTimestamp = null)
    {
        if (!isset($dayTimestamp)) {
            return $this->endTimestamp;
        }

        list($year, $month, $day) = explode(':', date('Y:n:j', $dayTimestamp));
        list($hour, $min, $sec) = explode(':', date('G:i:s', $this->endTimestamp));

        return mktime($hour, $min, $sec, $month, $day, $year);
    }

    function getEndDate($formatString, $dayTimestamp = null)
    {
        return date($formatString, $this->getEndTimestamp($dayTimestamp));
    }

    function setRecurEndTimestamp($recurTimestamp)
    {
        $this->recurEndTimestamp = $recurTimestamp;
    }

    function getRecurEndTimestamp()
    {
        return $this->recurEndTimestamp;
    }

    function hasRecurEnd()
    {
        return (isset($this->recurEnd) && isset($this->recurEnd->year) && $this->recurEnd->year != 9999);
    }

    function getRecurEndDate($formatString)
    {
        return date($formatString, $this->recurEndTimestamp);
    }

    function isAllDay()
    {
        return ($this->start->hour == 0 && $this->start->min == 0 && $this->start->sec == 0 &&
                $this->end->hour == 0 && $this->end->min == 0 && $this->start->sec == 0 &&
                ($this->end->mday > $this->start->mday ||
                 $this->end->month > $this->start->month ||
                 $this->end->year > $this->start->year));
    }

    function setAlarm($alarm)
    {
        $this->alarm = $alarm;
    }

    function getAlarm()
    {
        return $this->alarm;
    }

    function readForm()
    {
        global $prefs;

        // see if it's a new event
        if (is_null($this->getID())) {
            $this->setCreatorID(Auth::getAuth());
        }

        // Basic fields.
        $this->setTitle(Horde::getFormData('title', $this->title));
        $this->setDescription(Horde::getFormData('description', $this->description));
        $this->setLocation(Horde::getFormData('location', $this->location));
        $this->setKeywords(Horde::getFormData('keywords', $this->keywords));

        // Category.
        if (Horde::getFormData('category') == '*new*') {
            $category = Kronolith::addCategory(Horde::getFormData('new_category', $this->category));
        } else {
            $category = Horde::getFormData('category', $this->category);
        }
        $this->setCategory($category);

        // Event start.
        $start_year = Horde::getFormData('start_year');
        $start_month = Horde::getFormData('start_month');
        $start_day = Horde::getFormData('start_day');
        $start_hour = Horde::getFormData('start_hour');
        $start_min = Horde::getFormData('start_min');
        $am_pm = Horde::getFormData('am_pm');

        if (!$prefs->getValue('twentyFour')) {
            if ($am_pm == 'PM') {
                if ($start_hour != 12) {
                    $start_hour += 12;
                }
            } elseif ($start_hour == 12) {
                $start_hour = 0;
            }
        }

        if (Horde::getFormData('end_or_dur') == 1) {
            if (Horde::getFormData('whole_day') == 1) {
                $start_hour = 0;
                $start_min = 0;
                $dur_day = 0;
                $dur_hour = 24;
                $dur_min = 0;
            } else {
                $dur_day = Horde::getFormData('dur_day');
                $dur_hour = Horde::getFormData('dur_hour');
                $dur_min = Horde::getFormData('dur_min');
            }
        }

        $this->setStartTimestamp(mktime($start_hour, $start_min, 0,
                                        $start_month, $start_day,
                                        $start_year));

        if (Horde::getFormData('end_or_dur') == 1) {
            // Event duration.
            $this->setEndTimestamp(mktime($start_hour + $dur_hour,
                                          $start_min + $dur_min,
                                          0,
                                          $start_month,
                                          $start_day + $dur_day,
                                          $start_year));
        } else {
            // Event end.
            $end_year = Horde::getFormData('end_year');
            $end_month = Horde::getFormData('end_month');
            $end_day = Horde::getFormData('end_day');
            $end_hour = Horde::getFormData('end_hour');
            $end_min = Horde::getFormData('end_min');
            $end_am_pm = Horde::getFormData('end_am_pm');

            if (!$prefs->getValue('twentyFour')) {
                if ($end_am_pm == 'PM') {
                    if ($end_hour != 12) {
                        $end_hour += 12;
                    }
                } elseif ($end_hour == 12) {
                    $end_hour = 0;
                }
            }

            $endstamp = mktime($end_hour, $end_min, 0,
                               $end_month, $end_day, $end_year);
            if ($endstamp < $this->getStartTimestamp()) {
                $endstamp = $this->getStartTimestamp();
            }
            $this->setEndTimestamp($endstamp);
        }

        // Alarm.
        if (Horde::getFormData('alarm') == 1) {
            $this->setAlarm(Horde::getFormData('alarm_value') * Horde::getFormData('alarm_unit'));
        } else {
            $this->setAlarm(0);
        }

        // Recurrence.
        $recur = Horde::getFormData('recur');
        if (!is_null($recur) && $recur !== '') {
            if (Horde::getFormData('recur_enddate_type') == 'none') {
                $recur_enddate_year = 9999;
                $recur_enddate_month = 12;
                $recur_enddate_day = 31;
            } else {
                $recur_enddate_year = Horde::getFormData('recur_enddate_year');
                $recur_enddate_month = Horde::getFormData('recur_enddate_month');
                $recur_enddate_day = Horde::getFormData('recur_enddate_day');
            }

            $this->setRecurEndTimestamp(@mktime(1, 1, 1,
                                                $recur_enddate_month,
                                                $recur_enddate_day,
                                                $recur_enddate_year));

            $this->setRecurType($recur);
            switch ($recur) {
            case KRONOLITH_RECUR_DAILY:
                $this->setRecurInterval(Horde::getFormData('recur_daily_interval', 1));
                break;

            case KRONOLITH_RECUR_WEEKLY:
                $weekly = Horde::getFormData('weekly');
                $weekdays = 0;
                if (is_array($weekly)) {
                    foreach ($weekly as $day) {
                        $weekdays |= $day;
                    }
                }

                if ($weekdays == 0) {
                    // date('w') starts Sunday at 0.
                    switch ($this->getStartDate('w')) {
                    case 0: $weekdays |= KRONOLITH_MASK_SUNDAY; break;
                    case 1: $weekdays |= KRONOLITH_MASK_MONDAY; break;
                    case 2: $weekdays |= KRONOLITH_MASK_TUESDAY; break;
                    case 3: $weekdays |= KRONOLITH_MASK_WEDNESDAY; break;
                    case 4: $weekdays |= KRONOLITH_MASK_THURSDAY; break;
                    case 5: $weekdays |= KRONOLITH_MASK_FRIDAY; break;
                    case 6: $weekdays |= KRONOLITH_MASK_SATURDAY; break;
                    }
                }

                $this->setRecurInterval(Horde::getFormData('recur_weekly_interval', 1));
                $this->setRecurOnDay($weekdays);
                break;

            case KRONOLITH_RECUR_DAY_OF_MONTH:
                $this->setRecurInterval(Horde::getFormData('recur_day_of_month_interval', 1));
                break;

            case KRONOLITH_RECUR_WEEK_OF_MONTH:
                $this->setRecurInterval(Horde::getFormData('recur_week_of_month_interval', 1));
                break;

            case KRONOLITH_RECUR_YEARLY:
                $this->setRecurInterval(Horde::getFormData('recur_yearly_interval', 1));
                break;
            }
        }

        $this->initialized = true;
    }

    function getDuration()
    {
        static $duration;
        if (isset($duration)) {
            return $duration;
        }

        if ($this->isInitialized()) {
            $dur_day_match = $this->getEndDate('j') - $this->getStartDate('j');
            $dur_hour_match = $this->getEndDate('G') - $this->getStartDate('G');
            $dur_min_match = $this->getEndDate('i') - $this->getStartDate('i');
            while ($dur_min_match < 0) {
                $dur_min_match += 60;
                $dur_hour_match--;
            }
            while ($dur_hour_match < 0) {
                $dur_hour_match += 24;
                $dur_day_match--;
            }
            if ($dur_hour_match == 0 && $dur_min_match == 0
                && ($this->getEndDate('d') - $this->getStartDate('d')) == 1) {
                $dur_day_match = 0;
                $dur_hour_match = 23;
                $dur_min_match = 60;
                $whole_day_match = true;
            } else {
                $whole_day_match = false;
            }
        } else {
            $dur_day_match = 0;
            $dur_hour_match = 1;
            $dur_min_match = 0;
            $whole_day_match = false;
        }

        $duration = new stdClass();
        $duration->day = $dur_day_match;
        $duration->hour = $dur_hour_match;
        $duration->min = $dur_min_match;
        $duration->wholeDay = $whole_day_match;

        return $duration;
    }

    function html($property)
    {
        global $prefs;

        $options = array();
        $attributes = '';
        $sel = false;

        switch ($property) {
        case 'category':
            $cats = Kronolith::listCategories();
            foreach ($cats as $cat) {
                $options[$cat] = $cat;
            }
            $sel = $this->getCategory();
            break;

        case 'start_year':
            $sel = $this->getStartDate('Y');
            for ($i = -1; $i < 6; $i++) {
                $yr = date('Y') + $i;
                $options[$yr] = $yr;
            }
            $attributes = ' onchange="updateWday(\'start_wday\'); document.event.whole_day.checked = false; updateEndDate();"';
            break;

        case 'start_month':
            $sel = $this->getStartDate('n');
            for ($i = 1; $i < 13; $i++) {
                $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
            }
            $attributes = ' onchange="updateWday(\'start_wday\'); document.event.whole_day.checked = false; updateEndDate();"';
            break;

        case 'start_day':
            $sel = $this->getStartDate('j');
            for ($i = 1; $i < 32; $i++) {
                $options[$i] = $i;
            }
            $attributes = ' onchange="updateWday(\'start_wday\');document.event.whole_day.checked=false;updateEndDate();"';
            break;

        case 'start_hour':
            $sel = $this->getStartDate(($prefs->getValue('twentyFour')) ? 'G' : 'g');
            $hour_min = ($prefs->getValue('twentyFour')) ? 0 : 1;
            $hour_max = ($prefs->getValue('twentyFour')) ? 24 : 13;
            for ($i = $hour_min; $i < $hour_max; $i++) {
                $options[$i] = $i;
            }
            $attributes = ' onchange="document.event.whole_day.checked = false; updateEndDate();"';
            break;

        case 'start_min':
            $sel = $this->getStartDate('i');
            for ($i = 0; $i < 12; $i++) {
                $min = sprintf('%02d', $i * 5);
                $options[$min] = $min;
            }
            $attributes = ' onchange="document.event.whole_day.checked = false; updateEndDate();"';
            break;

        case 'end_year':
            $sel = $this->isInitialized() ? $this->getEndDate('Y') : $this->getStartDate('Y');
            for ($i = -1; $i < 6; $i++) {
                $yr = date('Y') + $i;
                $options[$yr] = $yr;
            }
            $attributes = ' onchange="updateWday(\'end_wday\'); updateDuration(); document.event.end_or_dur[0].checked = true"';
            break;

        case 'end_month':
            $sel = $this->isInitialized() ? $this->getEndDate('n') : $this->getStartDate('n');
            for ($i = 1; $i < 13; $i++) {
                $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
            }
            $attributes = ' onchange="updateWday(\'end_wday\'); updateDuration(); document.event.end_or_dur[0].checked = true"';
            break;

        case 'end_day':
            $sel = $this->isInitialized() ? $this->getEndDate('j') : $this->getStartDate('j');
            for ($i = 1; $i < 32; $i++) {
                $options[$i] = $i;
            }
            $attributes = ' onchange="updateWday(\'end_wday\'); updateDuration(); document.event.end_or_dur[0].checked = true"';
            break;

        case 'end_hour':
            $sel = $this->isInitialized() ?
                $this->getEndDate(($prefs->getValue('twentyFour')) ? 'G' : 'g') :
                $this->getStartDate(($prefs->getValue('twentyFour')) ? 'G' : 'g') + 1;
            $hour_min = $prefs->getValue('twentyFour') ? 0 : 1;
            $hour_max = $prefs->getValue('twentyFour') ? 24 : 13;
            for ($i = $hour_min; $i < $hour_max; $i++) {
                $options[$i] = $i;
            }
            $attributes = ' onchange="updateDuration(); document.event.end_or_dur[0].checked = true"';
            break;

        case 'end_min':
            $sel = $this->isInitialized() ? $this->getEndDate('i') : $this->getStartDate('i');
            for ($i = 0; $i < 12; $i++) {
                $min = sprintf('%02d', $i * 5);
                $options[$min] = $min;
            }
            $attributes = ' onchange="updateDuration(); document.event.end_or_dur[0].checked = true"';
            break;

        case 'dur_day':
            $dur = $this->getDuration();
            $sel = $dur->day;
            for ($i = 0; $i < 366; $i++) {
                $options[$i] = $i;
            }
            $attributes = ' onchange="document.event.whole_day.checked = false; updateEndDate(); document.event.end_or_dur[1].checked = true;"';
            break;

        case 'dur_hour':
            $dur = $this->getDuration();
            $sel = $dur->hour;
            for ($i = 0; $i < 24; $i++) {
                $options[$i] = $i;
            }
            $attributes = ' onchange="document.event.whole_day.checked = false; updateEndDate(); document.event.end_or_dur[1].checked = true;"';
            break;

        case 'dur_min':
            $dur = $this->getDuration();
            $sel = $dur->min;
            for ($i = 0; $i < 13; $i++) {
                $min = sprintf('%02d', $i * 5);
                $options[$min] = $min;
            }
            $attributes = ' onchange="document.event.whole_day.checked = false;updateEndDate();document.event.end_or_dur[1].checked=true"';
            break;

        case 'recur_enddate_year':
            if ($this->isInitialized()) {
                $sel = $this->hasRecurEnd() ? $this->recurEnd->year : $this->end->year;
            } else {
                $sel = $this->getStartDate('Y');
            }
            for ($i = -1; $i < 6; $i++) {
                $yr = date('Y') + $i;
                $options[$yr] = $yr;
            }
            $attributes = ' onchange="updateWday(\'recur_end_wday\'); document.event.recur_enddate_type[1].checked = true;"';
            break;

        case 'recur_enddate_month':
            if ($this->isInitialized()) {
                $sel = $this->hasRecurEnd() ? $this->recurEnd->month : $this->end->month;
            } else {
                $sel = $this->getStartDate('m');
            }
            for ($i = 1; $i < 13; $i++) {
                $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
            }
            $attributes = ' onchange="updateWday(\'recur_end_wday\'); document.event.recur_enddate_type[1].checked = true;"';
            break;

        case 'recur_enddate_day':
            if ($this->isInitialized()) {
                $sel = $this->hasRecurEnd() ? $this->recurEnd->mday : $this->end->mday;
            } else {
                $sel = $this->getStartDate('d');
            }
            for ($i = 1; $i < 32; $i++) {
                $options[$i] = $i;
            }
            $attributes = ' onchange="updateWday(\'recur_end_wday\'); document.event.recur_enddate_type[1].checked = true;"';
            break;
        }

        require_once HORDE_BASE . '/lib/Form/Renderer.php';
        $html = '<select name="' . $property . '"' . $attributes . ' id="' . $property . '">';
        $html .= Horde_Form_Renderer::_selectOptions($options, $sel);
        $html .= '</select>';

        return $html;
    }

    function getLink($timestamp = 0, $icons = true)
    {
        global $print_view, $prefs, $registry;

        $share = &$GLOBALS['shares']->getShare($this->getCalendar());
        $link = '';
        if (!is_a($share, 'PEAR_Error') && $share->hasPermission(Auth::getAuth(), _PERMS_READ, $this->getCreatorID())) {
            if (isset($this->remoteCal)) {
                $url = Horde::addParameter('viewevent.php', 'eventID=' . $this->eventIndex);
                $url = Horde::addParameter($url, 'calendar=**remote');
                $url = Horde::addParameter($url, 'remoteCal=' . $this->remoteCal);
                $url = Horde::addParameter($url, 'timestamp=' . $timestamp);
                $link .= Horde::link(Horde::applicationUrl($url), $this->getTitle(), 'event', '', '', $this->description);
            } elseif (isset($this->eventID)) {
                $url = Horde::addParameter('viewevent.php', 'eventID=' . $this->eventID);
                $url = Horde::addParameter($url, 'calendar=' . $this->getCalendar());
                $url = Horde::addParameter($url, 'timestamp=' . $timestamp);
                $link .= Horde::link(Horde::applicationUrl($url), $this->title, 'event', '', '', $this->description);
            } elseif (isset($this->taskID)) {
                $link .= Horde::link(Horde::url($GLOBALS['registry']->link('tasks/show', array('task' => $this->taskID, 'tasklist' => $this->tasklistID))), $this->title, 'event');
            } elseif (isset($this->meetingID)) {
                $link .= Horde::link(Horde::url($GLOBALS['registry']->link('meeting/show', array('meeting' => $this->meetingID))), $this->title, 'event');
            }
        }

        $link .= @htmlspecialchars($this->getTitle(), ENT_QUOTES, NLS::getCharset());

        if (!is_a($share, 'PEAR_Error') && $share->hasPermission(Auth::getAuth(), _PERMS_READ, $this->getCreatorID()) &&
            (isset($this->eventID) || isset($this->taskID) || isset($this->remoteCal))) {
            $link .= '</a>';
        }

        if ($icons && $prefs->getValue('show_icons')) {
            if ($this->alarm) {
                $link .= Horde::img('alarm_small.gif', sprintf(_("%s Minutes before"), $this->alarm));
            }
            if (!$print_view) {
                if (!is_a($share, 'PEAR_Error') && $share->hasPermission(Auth::getAuth(), _PERMS_DELETE, $this->getCreatorID())) {
                    if (isset($this->eventID)) {
                        $url = Horde::addParameter('editevent.php', 'eventID', $this->eventID);
                        $url = Horde::addParameter($url, 'calendar', $this->getCalendar());
                        $url = Horde::addParameter($url, 'timestamp', $timestamp);
                        $url = Horde::addParameter($url, 'url', Horde::selfURL(true));
                        $link .= '&nbsp;' . Horde::link(Horde::applicationUrl($url), sprintf(_("Edit %s"), $this->title)) . Horde::img('edit.gif', sprintf(_("Edit %s"), @htmlspecialchars($this->getTitle(), ENT_QUOTES, NLS::getCharset())), null, $registry->getParam('graphics', 'horde')) . '</a>';

                        $url = Horde::addParameter('delevent.php', 'eventID', $this->eventID);
                        $url = Horde::addParameter($url, 'calendar', $this->getCalendar());
                        $url = Horde::addParameter($url, 'timestamp', $timestamp);
                        $url = Horde::addParameter($url, 'url', Horde::selfURL(true));
                        $link .= '&nbsp;' . Horde::link(Horde::applicationUrl($url), sprintf(_("Delete %s"), $this->title)) . Horde::img('delete.gif', sprintf(_("Delete %s"), @htmlspecialchars($this->getTitle(), ENT_QUOTES, NLS::getCharset())), null, $registry->getParam('graphics', 'horde')) . '</a>';
                    }
                }
            }

            if (!$this->hasRecurType(KRONOLITH_RECUR_NONE)) {
                $link .= Horde::img('recur.gif', Kronolith::recurToString($this->recurType));
            }
        }
        return $link;
    }

    function nextRecurrence($afterDate, $weekstart = KRONOLITH_SUNDAY)
    {
        $driver = &$this->getDriver();
        return $driver->nextRecurrence($this->eventID, $afterDate, $weekstart);
    }

    function getCalendar()
    {
        return $this->_calendar;
    }

}
