<?php

/**
 * CalDAV calendaring connection class
 * 
 * This adapter is specifically tailored for the Atmail built-in CalDAV server. 
 * This adapter adds a couple of non-standard features, specifically related to 
 * per-calendar delegation, as well as several optimizations.
 *
 * @package Atmail
 * @subpackage Calendar 
 * @copyright Copyright (C) 2011 Atmail. All rights reserved.
 * @author Evert Pot (http://www.rooftopsolutions.nl/) 
 */
class Atmail_Calendar_Adapter_IntegratedCalDAV extends Atmail_Calendar_Adapter_CalDAV implements Atmail_Calendar_Adapter_IDelegation {

    /**
     * Constructor 
     *
     * The url, userName and password settings are required
     *
     * @param array $settings 
     */
    public function __construct(array $settings) {

        $required = array(
            'userName',
            'password',
        );

        foreach($required as $rr) {
            if (!isset($settings[$rr])) {
                throw new InvalidArgumentException('The ' . $rr . ' setting is required');
            }
        }
        
        $this->_dav = new Atmail_Dav_LocalClient(array(
            'userName' => $settings['userName'],
            'password' => $settings['password'],
        ));

        $this->_userName = $settings['userName'];

        $this->_dav->propertyMap['{DAV:}current-user-principal'] = 'Sabre_DAV_Property_Href';
        $this->_dav->propertyMap['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-home-set'] = 'Sabre_DAV_Property_HrefList';
   	    $this->_cachePrefix = get_class($this) . ':' . md5($settings['url'] . ':' . $settings['userName'] . ':' . $settings['password']) . ':';
    }

	public function fetchDelegateFromID($id) {

		// Because there is no true API to SabreDAV to find per calendar who
		// has access, we're going to do direct database operations.
		$dbAdapter = Zend_Registry::get('dbAdapter');
		$query = "SELECT * FROM calendars WHERE id = ?";
		$result = $dbAdapter->query($query, array($id));
		$result = $result->fetchAll();

		return isset($result[0]) ? $result[0] : array();

	}

    /**
     * Returns a list of members for a specific calendar.
     *
     * The response to this method are two arrays, wrapped in an array.
     * The first array contains members (atmail email addresses) that have 
     * read-only access to the calendar. The second array contains members that 
     * have read-write access.
     *
     * @param Atmail_Calendar_Calendar $calendar 
     * @return array 
     */ 
    public function getMembers(Atmail_Calendar_Calendar $calendar, $username = null, $targetUri = null) {

        // Because there is no true API to SabreDAV to find per calendar who 
        // has access, we're going to do direct database operations.
		$query = "
            SELECT 
                p1.uri, calendarDelegates.mode 
            FROM calendarDelegates
            LEFT JOIN principals AS p1 ON p1.id = calendarDelegates.principalid
            LEFT JOIN calendars ON calendars.id = calendarDelegates.calendarid
            WHERE calendars.principaluri = ? AND calendars.uri = ?
            ";

		if($username == null)
		{
			$username = $this->_userName;
		}

		$targetUri = $targetUri != null ? $targetUri : basename($calendar->url);

        $dbAdapter = Zend_Registry::get('dbAdapter');
        $result = $dbAdapter->query($query, array("principals/users/" . $username, $targetUri));
		$result = $result->fetchAll();

        $read = array();
        $write = array();

        foreach($result as $row) {
            if ($row['mode'] == 1) {
                $read[] = basename($row['uri']);
            } else {
                $write[] = basename($row['uri']);
            }
        }
        
        return array($read, $write);

    }
    
    public function provisionMember(Atmail_Calendar_Calendar $calendar, $member)
    {
    	$result = array();
    	$dbAdapter = Zend_Registry::get('dbAdapter');
    	
    	$result['account'] = $dbAdapter->query('SELECT Account FROM Users where Account = ?', array($member))->fetchAll();
		if (!count($result['account']))
		{
			throw new Exception("Account " . $member . ' does not exist in Users table'); 
		}
		$result['account'] = $result['account'][0];
		
		// Adding the main principal
		$result['principal'] = $dbAdapter->query('SELECT * FROM principals where uri = ? and email = ?', array('principals/users/' . $member, $member))->fetchAll();
       	if (!count($result['principal']))
		{
			$dbAdapter->query("INSERT IGNORE INTO principals (uri,email,displayname) VALUES (?, ?, ?)", array('principals/users/' . $member, $member, $member));
			$result['principal'] = array( array( 'uri' => 'principals/users/' . $member, 'email' => $member, 'displayname' => $member, 'id' => $dbAdapter->lastInsertId() ) );
		}
		$result['principal'] = $result['principal'][0];
		
		$result['calendars'] = $dbAdapter->query('SELECT * FROM calendars where principaluri = ? and displayname = ? and uri = ?', array('principals/users/' . $member, 'Private','calendar'))->fetchAll();
		if (!count($result['calendars']))
		{
			$dbAdapter->query(
               	'INSERT IGNORE INTO calendars (principaluri, displayname, uri, description, components, ctag, calendarcolor) VALUES (?, ?, ?, "", "VEVENT,VTODO", 1, "#0002BFFF")',
                   array('principals/users/' . $member, 'Private','calendar')
               );
           	$result['calendars'] = array( array('principaluri' => 'principals/users/' . $member, 'displayname' => 'Private', 'uri' => 'calendar', 'id' => $dbAdapter->lastInsertId()) );
		}
		$result['calendars'] = $result['calendars'][0];	
		
		return $result;
    }
    
    /**
     * Updates the list of members for a specific calendar
     *
     * @param Atmail_Calendar_Calendar $calendar
     * @param array $members
     */ 
    public function setMembers(Atmail_Calendar_Calendar $calendar, array $readMembers, array $writeMembers, $username = null, $targetUri = null)
    { 
    	$members = array();
        $dbAdapter = Zend_Registry::get('dbAdapter');

		if($username == null)
		{
			$username = $this->_userName;
		}
		$targetUri = $targetUri != null ? $targetUri : basename($calendar->url);
        // Calendar id
        $result = $dbAdapter->query(
            "SELECT id FROM calendars 
            WHERE calendars.uri = ? AND calendars.principaluri = ?",
            array($targetUri, 'principals/users/' . $username)
        );
        $result = $result->fetchAll();
        if (!$result)
        {
            throw new Atmail_Calendar_Exception('Could not find this atmail calendar in the database');
        }       
        $calendarId = $result[0]['id'];
       
        // Removing old delegates
        $query = "DELETE FROM calendarDelegates WHERE calendarid = ?";
        $dbAdapter->query($query, array($calendarId)); 
			
		foreach($readMembers as $member)
		{
			$members[] = array_merge($this->provisionMember($calendar, $member), array('mode' => 1));
		}
		
		foreach($writeMembers as $member)
		{
			$members[] = array_merge($this->provisionMember($calendar, $member), array('mode' => 2));
		}
			
		$query = "INSERT IGNORE INTO calendarDelegates (calendarid, principalid, mode) SELECT ?, principals.id, ? FROM principals WHERE uri = ?";
        foreach($members as $member)
        {
        	// test for duplicates
        	$result = $dbAdapter->query('SELECT * FROM calendarDelegates where calendarid = ? and principalid = ? and mode = ?', array($calendarId, $member['principal']['id'], $member['mode']))->fetchAll();
        	if (!count($result))
        	{
        		$dbAdapter->query($query, array($calendarId, $member['mode'], $member['principal']['uri']));
        	}
        }
    }

    /**
     * This method is responsible for fetching both the calendarHomeSet and 
     * principalUrl information. 
     *
     * This is completely hardcoded in the 'integrated' version for speed.
     * 
     * @return void
     */
    protected function fetchAccountUrls() {

        if ($this->_calendarHomeSet && $this->_principalUrl) {
            return;
        }

        $this->_calendarHomeSet = array(
            '/calendars/' . urlencode($this->_userName) . '/',
        ); 
        $this->_principalUrl = '/principals/users/' . urlencode($this->_userName) . '/';

    }

}
