<?php
/**
 * The Kronolith_Driver_mcal:: class implements the Kronolith_Driver
 * API for an MCAL backend.
 *
 * $Horde: kronolith/lib/Driver/mcal.php,v 1.62.2.1 2005/04/21 11:27:03 selsky Exp $
 *
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @since   Kronolith 0.1
 * @package Kronolith
 */
class Kronolith_Driver_mcal extends Kronolith_Driver {

    /**
     * The current MCAL connection.
     *
     * @var resource $_stream
     */
    var $_stream;

    function open($calendar)
    {
        $this->_calendar = $calendar;
        $this->_stream = @mcal_popen("{/mstore}<$calendar>",
                                     $this->_params['username'],
                                     $this->_params['password']);
    }

    function close()
    {
        @mcal_close($this->_stream);
    }

    function listEvents($startDate = null, $endDate = null)
    {
        require_once 'Horde/Array.php';

        $events = @mcal_list_events($this->_stream, $startDate->year, $startDate->month, $startDate->mday, $endDate->year, $endDate->month, $endDate->mday);
        return is_array($events) ?
            Horde_Array::combine(array_map(create_function('$e', 'return "kronolith:' . $this->_calendar . ':" . $e;'), $events), $events) :
            array();
    }

    function listAlarms($date)
    {
        $events = @mcal_list_alarms($this->_stream, $date->year, $date->month, $date->mday, $date->hour, $date->min, $date->sec);
        return is_array($events) ? $events : array();
    }

    function &getEvent($eventID = null)
    {
        if (!is_null($eventID)) {
            $event = @mcal_fetch_event($this->_stream, (int)$eventID);
            if ($event && $event->id > 0) {
                return $eventOb = &new Kronolith_Event_mcal($this, $event);
            } else {
                return false;
            }
        } else {
            return $eventOb = &new Kronolith_Event_mcal($this);
        }
    }

    function &getByUID($uid)
    {
        return PEAR::raiseError('Not supported');
    }

    function saveEvent($event)
    {
        if (!is_null($event->getID())) {
            if ($id = mcal_store_event($this->_stream)) {
                return $event->getID();
            } else {
                return false;
            }
        } else {
            if ($id = mcal_append_event($this->_stream)) {
                return $id;
            } else {
                return false;
            }
        }
    }

    function &nextRecurrence($eventID, $afterDate)
    {
        $next = mcal_next_recurrence($this->_stream, HORDE_DATE_SUNDAY, $afterDate);
        if (empty($next->year)) {
            return false;
        }

        return $date = &new Horde_Date($next);
    }

    /**
     * Move an event to a new calendar.
     *
     * @param string $eventId      The event to move.
     * @param string $newCalendar  The new calendar.
     */
    function move($eventId, $newCalendar)
    {
        $event = &$this->getEvent($eventId);
        if (!$event) {
            return PEAR::raiseError('not found');
        }

        $event->setCalendar($newCalendar);
        return $event->save();
    }

    /**
     * Delete a calendar and all its events.
     *
     * @param string $calendar The name of the calendar to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function delete($calendar)
    {
        /**
         * @TODO FIXME: this is horrid, but will work for mstore for
         * now.
         */
        $file = '/var/calendar/' . basename($calendar);

        $this->close();

        @unlink($file);
        if (!@file_exists($file)) {
            $result = true;
        } else {
            $result = PEAR::raiseError(sprintf(_("Unable to delete %s."), $calendar));
        }

        return $result;
    }

    function deleteEvent($eventID)
    {
        return mcal_delete_event($this->_stream, $eventID);
    }

}

class Kronolith_Event_mcal extends Kronolith_Event {

    function toDriver()
    {
        $driver = &$this->getDriver();

        // Basic fields.
        mcal_event_set_title($driver->_stream, $this->getTitle());
        mcal_event_set_description($driver->_stream, $this->getDescription());
        mcal_event_set_category($driver->_stream, $this->getCategory());
        mcal_event_add_attribute($driver->_stream, 'location', $this->getLocation());
        mcal_event_add_attribute($driver->_stream, 'keywords', implode(',', $this->getKeywords()));
        mcal_event_add_attribute($driver->_stream, 'exceptions', implode(',', $this->getExceptions()));
        mcal_event_add_attribute($driver->_stream, 'modified', time());
        mcal_event_add_attribute($driver->_stream, 'creatorid', $this->getCreatorID());

        // Event start.
        mcal_event_set_start($driver->_stream, $this->start->year, $this->start->month, $this->start->mday, $this->start->hour, $this->start->min);

        // Event end.
        mcal_event_set_end($driver->_stream, $this->end->year, $this->end->month, $this->end->mday, $this->end->hour, $this->end->min);

        // Alarm.
        mcal_event_set_alarm($driver->_stream, $this->getAlarm());

        // Recurrence.
        if (empty($this->recurEnd)) {
            $recurEnd = &new Horde_Date(array('year' => 9999, 'month' => 12, 'mday' => 31));
        } else {
            $recurEnd = $this->recurEnd;
        }

        switch ($this->getRecurType()) {
        case KRONOLITH_RECUR_NONE:
            mcal_event_set_recur_none($driver->_stream);
            break;

        case KRONOLITH_RECUR_DAILY:
            mcal_event_set_recur_daily($driver->_stream,
                                       $recurEnd->year,
                                       $recurEnd->month,
                                       $recurEnd->mday,
                                       $this->getRecurInterval());
            break;

        case KRONOLITH_RECUR_WEEKLY:
            mcal_event_set_recur_weekly($driver->_stream,
                                        $recurEnd->year,
                                        $recurEnd->month,
                                        $recurEnd->mday,
                                        $this->getRecurInterval(),
                                        $this->getRecurOnDays());
            break;

        case KRONOLITH_RECUR_DAY_OF_MONTH:
            mcal_event_set_recur_monthly_mday($driver->_stream,
                                              $recurEnd->year,
                                              $recurEnd->month,
                                              $recurEnd->mday,
                                              $this->getRecurInterval());
            break;

        case KRONOLITH_RECUR_WEEK_OF_MONTH:
            mcal_event_set_recur_monthly_wday($driver->_stream,
                                              $recurEnd->year,
                                              $recurEnd->month,
                                              $recurEnd->mday,
                                              $this->getRecurInterval());
            break;

        case KRONOLITH_RECUR_YEARLY:
            mcal_event_set_recur_yearly($driver->_stream,
                                        $recurEnd->year,
                                        $recurEnd->month,
                                        $recurEnd->mday,
                                        $this->getRecurInterval());
            break;
        }
    }

    function fromDriver($mcalEvent)
    {
        $this->title = $mcalEvent->title;
        if (isset($mcalEvent->category)) {
            $this->category = $mcalEvent->category;
        }
        if (isset($mcalEvent->description)) {
            $this->description = $mcalEvent->description;
        }
        if (isset($mcalEvent->attrlist['creatorid'])) {
            $this->creatorID = $mcalEvent->attrlist['creatorid'];
        }
        if (isset($mcalEvent->attrlist['location'])) {
            $this->location = $mcalEvent->attrlist['location'];
        }
        if (isset($mcalEvent->attrlist['keywords'])) {
            $this->keywords = explode(',', $mcalEvent->attrlist['keywords']);
        }
        if (isset($mcalEvent->attrlist['exceptions'])) {
            $this->exceptions = explode(',', $mcalEvent->attrlist['exceptions']);
        }
        $this->eventID = $mcalEvent->id;

        $this->start = $this->parseMCALDate($mcalEvent->start);
        $this->end = $this->parseMCALDate($mcalEvent->end);

        $this->durMin = ($this->end->timestamp() - $this->start->timestamp()) / 60;

        if (isset($mcalEvent->recur_enddate)) {
            $this->recurEnd = $this->parseMCALDate($mcalEvent->recur_enddate);
        }

        $this->alarm = $mcalEvent->alarm;

        $this->recurType = $mcalEvent->recur_type;
        $this->recurInterval = $mcalEvent->recur_interval;
        if (isset($mcalEvent->recur_data)) {
            $this->recurData = $mcalEvent->recur_data;
        }

        $this->initialized = true;
        $this->stored = true;
    }

    function parseMCALDate($date)
    {
        if (!count($date)) {
            return null;
        }

        $dateOb = &new Horde_Date($date);

        // Check for events with no recur_enddate.
        if ($date->year == 9999 && $date->month == 12 && $date->mday == 31) {
            return null;
        }

        return $dateOb;
    }

}
