<?php


/**
 * The Kronolith_Driver_webdav:: class implements the Kronolith_Driver  
 * API for a SQL backend. All database operations are handeled by the
 * superclass Kronolith_Driver_sql
 *
 * $Id$
 *
 * @author  Johan Ekblad <jka@eminds.se>
 * @author  Luc Saillard <luc.saillard@fr.alcove.com>
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @since   Kronolith 0.3
 * @package Kronolith
 */



class Kronolith_Driver_webdav extends Kronolith_Driver_sql {



    /**
     * The object handle for the current database connection.
     *
     * @var DB
     */
    var $_db;

    /**
     * Handle for the current database connection, used for writing. Defaults
     * to the same handle as $_db if a separate write database is not required.
     *
     * @var DB
     */
    var $_write_db;

    /**
     * Cache events as we fetch them to avoid fetching the same event from the
     * DB twice.
     *
     * @var array
     */
    var $_cache = array();

    function listAlarms($date)
    {
        return parent::listAlarms($date);
    }

    function search($query)
    {
        return parent::search($query);
    }

    /**
     * Checks if the event's UID already exists and returns all event
     * ids with that UID.
     *
     * @param string $uid          The event's uid.
     * @param string $calendar_id  Calendar to search in.
     *
     * @return string|boolean  Returns a string with event_id or false if
     *                         not found.
     */
    function exists($uid, $calendar_id = null)
    {
      //$this->saveUpdatesFromWebDAV("exists", $calendar_id, false);
        return parent::exists($uid, $calendar_id);
    }

    /**
     * Lists all events in the time range, optionally restricting
     * results to only events with alarms.
     *
     * @param Horde_Date $startInterval  Start of range date object.
     * @param Horde_Date $endInterval    End of range data object.
     * @param boolean $hasAlarm          Only return events with alarms?
     *                                   Defaults to all events.
     *
     * @return array  Events in the given time range.
     */
    function listEvents($startDate = null, $endDate = null, $hasAlarm = false)
    {
        $this->saveUpdatesFromWebDAV("listEvents", $this->_calendar, false);
        return parent::listEvents($startDate, $endDate, $hasAlarm);
    }

    /**
     * Lists all events that satisfy the given conditions.
     *
     * @param Horde_Date $startInterval  Start of range date object.
     * @param Horde_Date $endInterval    End of range data object.
     * @param string $conditions         Conditions, given as SQL clauses.
     * @param array $vals                SQL bind variables for use with
     *                                   $conditions clauses.
     *
     * @return array  Events in the given time range satisfying the given
     *                conditions.
     */
    function listEventsConditional($startInterval, $endInterval,
                                   $conditions = '', $vals = array())
    {
        return parent::listEventsConditional($startInterval, $endInterval, $conditions, $vals);
    }

    function &getEvent($eventId = null)
    {
        return parent::getEvent($eventId);
    }

    function &getByUID($uid)
    {
        return parent::getByUID($uid);
    }

    /**
     * Saves an event in the backend.
     * If it is a new event, it is added, otherwise the event is updated.
     *
     * @param Kronolith_Event $event  The event to save.
     */
    function saveEvent($event)
    {
      
        $this->saveUpdatesFromWebDAV("saveEvent", $this->_calendar, true);
        $eventId = parent::saveEvent($event);
        $this->saveUpdateToWebDAV("saveEvent", $this->_calendar);
	
        return $eventId;
      
    }

    /**
     * Move an event to a new calendar.
     *
     * @param string $eventId      The event to move.
     * @param string $newCalendar  The new calendar.
     */
    function move($eventId, $newCalendar)
    {
        $this->saveUpdatesFromWebDAV("move",$this->_calendar, true);
        $this->saveUpdatesFromWebDAV("move",$newCalendar, true);

	$res = parent::move($eventId, $newCalendar);

        $this->saveUpdateToWebDAV("move",$this->_calendar);
        $this->saveUpdateToWebDAV("move",$newCalendar);

	return $res;
    }

    /**
     * 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)
    {

        $this->saveUpdatesFromWebDAV("delete",$calendar, true);

        $result = parent::delete($calendar);

	$this->saveUpdateToWebDAV("delete",$calendar);

        return $result;
    }

    /**
     * Delete an event.
     *
     * @param string $eventId  The ID of the event to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function deleteEvent($eventId)
    {

        $this->saveUpdatesFromWebDAV("deleteEvent",$this->_calendar, true);

        $is_deleted = parent::deleteEvent($eventId);

        $this->saveUpdateToWebDAV("deleteEvent",$this->_calendar);
          
        return $is_deleted;

    }

    /**
     * Attempts to open a persistent connection to the SQL server.
     *
     * @return boolean True.
     */
    function initialize()
    {
        return parent::initialize();
    }

    function _initConn(&$db)
    {
        return parent::_initConn($db);
    }

    /**
     */
    function close()
    {
        return parent::close();
    }

    /**
     * Converts a value from the driver's charset to the default
     * charset.
     *
     * @param mixed $value  A value to convert.
     *
     * @return mixed  The converted value.
     */
    function convertFromDriver($value)
    {
        return parent::convertFromDriver($value);
    }

    /**
     * Converts a value from the default charset to the driver's
     * charset.
     *
     * @param mixed $value  A value to convert.
     *
     * @return mixed  The converted value.
     */
    function convertToDriver($value)
    {
        return parent::convertToDriver($value);
    }

    /**
     * Read iCalendar info from WebDAV source and update local database.
     * All UID:s that does not exist in the local database are created. 
     * All UID:s where some data differs are updated in the local database.
     *
     * @param mixed $operation - operation from which this method was called.
     *                           Just used for logging purpose.
     * @param mixed $calendar_id - the calendar.
     * @param mixed $lock_resource - true if the WebDAV source should be locked.
     *
     * @return mixed  true if everything went OK
     */
  function saveUpdatesFromWebDAV($operation, $calendar_id, $lock_resource)
  {
        require_once "HTTP/WebDAV/Client.php";
        require_once 'Horde/iCalendar.php';

	Horde::logMessage(sprintf('Save updates from WebDAV calendar %s%s.%s - operation=%s - lock=%b',$GLOBALS['conf']['calendar']['webdavurl'], $calendar_id, $GLOBALS['conf']['calendar']['webdavext'],$operation, $lock_resource),
			  __FILE__, __LINE__, PEAR_LOG_DEBUG);
	
	$url = $GLOBALS['conf']['calendar']['webdavurl'] . $calendar_id . "." . $GLOBALS['conf']['calendar']['webdavext'];
          

	$dh = fopen($url,"r",true);              
	if (!$dh)
        {
	    Horde::logMessage(sprintf('Cannot open WebDAV calendar with url=%s',$url),
			      __FILE__, __LINE__, PEAR_LOG_WARNING);
	    return false;
	}

	$res=""; 
	while (false != ($buf = fread($dh,4096))) {
	    $res=$res . $buf;
	}

	fclose($dh);

 	$data = strstr($res, "BEGIN");

	Horde::logMessage(sprintf('Got WebDAV data'),
		 	  __FILE__, __LINE__, PEAR_LOG_DEBUG);

        $iCal = &new Horde_iCalendar();
        if (!$iCal->parsevCalendar($data)) {
	    Horde::logMessage(sprintf('Error parseing iCalendar data'),
			      __FILE__, __LINE__, PEAR_LOG_WARNING);
	  return false;
        }

        $components = $iCal->getComponents();
        $count = count($components);
        for ($i = 0; $i < $count; $i++) {
            $component = $components[$i];
            if ($component->getType() == 'vEvent') {
                $wEvent = &new Kronolith_Event_webdav($this);
                $wEvent->fromiCalendar($component);

		$dbEvent=$this->getByUID($wEvent->getUID());

		if (is_a($dbEvent, 'PEAR_Error')) {
		    // UID does not exist, create in database.
		    Horde::logMessage(sprintf('WebDAV UID (%s) does not exist in db, create it',$wEvent->getUID()),
				    __FILE__, __LINE__, PEAR_LOG_DEBUG);
		    $wEvent->toDriver();
		    parent::saveEvent($wEvent);
                }
		else {
		    // Check if the fields are identical.

		    $wEvent->setId($dbEvent->getId());
		    $wEvent->toDriver();

		    // TODO: possibly include more fields to check here
		    if ($wEvent->getTitle() == $dbEvent->getTitle() &&
		      $wEvent->getDescription() == $dbEvent->getDescription() &&
		      $wEvent->getCategory() == $dbEvent->getCategory() &&
		      $wEvent->getLocation() == $dbEvent->getLocation() &&
		      $wEvent->getStatus() == $dbEvent->getStatus() &&
		      $wEvent->start == $dbEvent->start &&
		      $wEvent->end == $dbEvent->end) {

		        Horde::logMessage(sprintf('WebDAV UID (%s) identical - ignore',$wEvent->getUID()),
					  __FILE__, __LINE__, PEAR_LOG_DEBUG);

		    }
		    else
		    {
		        Horde::logMessage(sprintf('WebDAV UID (%s) differs, update database',$wEvent->getUID()),
					  __FILE__, __LINE__, PEAR_LOG_DEBUG);
		      
		      		      
			parent::saveEvent($wEvent);

		    }
		}
            }
        }

	return true;
   }

    /**
     * Update WebDAV source with the calendar info from the local database.
     *
     * @param mixed $operation - operation from which this method was called.
     *                           Just used for logging purpose.
     * @param mixed $calendar_id - the calendar.
     *
     * @return mixed  true if everything went OK
     */
  function saveUpdateToWebDAV($operation, $calendar_id)
  {
        require_once "HTTP/WebDAV/Client.php";
        require_once KRONOLITH_BASE . '/lib/base.php';
        require_once 'Horde/Data.php';
	require_once 'Horde/iCalendar.php';

	Horde::logMessage(sprintf('Save update to WebDAV calendar %s%s.%s - operation=%s', $GLOBALS['conf']['calendar']['webdavurl'],$calendar_id, $GLOBALS['conf']['calendar']['webdavext'], $operation),
			  __FILE__, __LINE__, PEAR_LOG_DEBUG);
	
	$url = $GLOBALS['conf']['calendar']['webdavurl'] . $calendar_id . "." . $GLOBALS['conf']['calendar']['webdavext'];
          
	$iCal = &new Horde_iCalendar();

        $eventIds = parent::listEvents(null,null,false);

        foreach ($eventIds as $eventId) {
	  $event = &$this->getEvent($eventId);
	  $iCal->addComponent($event->toiCalendar($iCal));
	}

	$data = $iCal->exportvCalendar();

	$dh = fopen($url,"w",true);
	
	if (!$dh)
        {
	    Horde::logMessage(sprintf('Cannot open WebDAV calendar for writing, url=%s',$url),
			      __FILE__, __LINE__, PEAR_LOG_WARNING);
	    return false;
	}

	if (fwrite($dh,$data) == false)
	{
	    Horde::logMessage(sprintf('Error writing WebDAV calendar, url=%s',$url),
			      __FILE__, __LINE__, PEAR_LOG_WARNING);
	    return false;
	}

	fclose($dh);


	return true;
  }
 


}

/**
 * @package Kronolith
 */
class Kronolith_Event_webdav extends Kronolith_Event_sql {

    /**
     * @var array
     */
    var $_properties = array();

    function fromDriver($SQLEvent)
    {
        return parent::fromDriver($SQLEvent);
    }

    function toDriver()
    {
        return parent::toDriver(); 
    }

    function getProperties()
    {
        return $this->_properties;
    }

}