<?php

include_once 'Horde/SyncML/Command.php';
include_once 'Horde/SyncML/Command/SyncElement.php';

/**
 * $Horde: framework/SyncML/SyncML/Command/Sync.php,v 1.17.10.3 2005/07/07 14:59:01 chuck Exp $
 *
 * Copyright 2005 Karsten Fourmont <karsten@horde.org>
 *
 * The command handler for the &gt;Sync&lt; command is the central
 * class to dispatch sync messages.
 *
 * During parsing of the received XML, the actual sync commands (Add, Replace,
 * Delete) from the client are stored in the _syncElements attribute.
 * When the output method of Horde_SyncML_Command_Sync is called, these
 * elements are processed and the resulting status messages created.
 *
 * Then the server modifications are sent back to the client by the
 * handleSync method which is called from within the output method.
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Karsten Fourmont <karsten@horde.org>
 * @since   Horde 3.0
 * @package Horde_SyncML
 */
class Horde_SyncML_Command_Sync extends Horde_Syncml_Command {

    var $_currentSyncElement;
    var $_syncElements = array();

    function output($currentCmdID, &$output)
    {
        global $registry, $backend;

        $state = &$_SESSION['SyncML.state'];
        $device = &$state->getDevice();

        $attrs = array();

        $sync = $state->getSync($this->_targetURI);

        // Here's where client modifications are processed:
        foreach ($this->_syncElements as $element) {
            foreach ($element->getItems() as $item) {
                $result = $sync->handleSyncItem($item);
            }
            $currentCmdID = $element->output($currentCmdID, $output);
        }

        // We create a seperate Sync Element for the Sync Data sent
        // from the Server to the client as we want to process the
        // client sync information before.
        $output->startElement($state->getURI(), 'Sync', $attrs);
        $output->startElement($state->getURI(), 'CmdID', $attrs);
        $output->characters($currentCmdID);
        $currentCmdID++;
        $output->endElement($state->getURI(), 'CmdID');

        $output->startElement($state->getURI(), 'Target', $attrs);
        $output->startElement($state->getURI(), 'LocURI', $attrs);
        $chars = $this->_sourceURI;
        $output->characters($chars);
        $output->endElement($state->getURI(), 'LocURI');
        $output->endElement($state->getURI(), 'Target');

        $output->startElement($state->getURI(), 'Source', $attrs);
        $output->startElement($state->getURI(), 'LocURI', $attrs);
        $chars = $this->_targetURI;
        $output->characters($chars);
        $output->endElement($state->getURI(), 'LocURI');
        $output->endElement($state->getURI(), 'Source');


        $syncType = $sync->_targetLocURI;

        // Here's where server modifications are sent to the client:
        $refts = $state->getServerAnchorLast($syncType);
        $currentCmdID = $sync->handleSync($currentCmdID, $syncType,
                                          $output, $refts);

        $output->endElement($state->getURI(), 'Sync');

        $status = &new Horde_SyncML_Command_Status(RESPONSE_OK, 'Sync');
        $status->setCmdRef($this->_cmdID);

        if ($this->_targetURI != null) {
            $status->setTargetRef($this->_targetURI);
        }

        if ($this->_sourceURI != null) {
            $status->setSourceRef($this->_sourceURI);
        }

        return $status->output($currentCmdID, $output);
    }

    function getTargetURI()
    {
        return $this->_targetURI;
    }

    function startElement($uri, $element, $attrs)
    {
        parent::startElement($uri, $element, $attrs);

        switch (count($this->_Stack)) {
        case 2:
            if ($element == 'Replace' || $element == 'Add' || $element == 'Delete') {
                $this->_currentSyncElement = &new Horde_SyncML_Command_SyncElement($element);
            }
            break;
        }

        if (isset($this->_currentSyncElement)) {
            $this->_currentSyncElement->startElement($uri, $element, $attrs);
        }
    }

    function endElement($uri, $element)
    {
        if (isset($this->_currentSyncElement)) {
            $this->_currentSyncElement->endElement($uri, $element);
        }

        switch (count($this->_Stack)) {
        case 2:
            if ($element == 'Replace' || $element == 'Add' || $element == 'Delete') {
                $this->_syncElements[] = $this->_currentSyncElement;
                unset($this->_currentSyncElement);
            }
            break;

        case 3:
            if ($element = 'LocURI' && !isset($this->_currentSyncElement)) {
                if ($this->_Stack[1] == 'Source') {
                    $this->_sourceURI = trim($this->_chars);
                } elseif ($this->_Stack[1] == 'Target') {
                    $this->_targetURI = preg_replace('/\?.*$/', '', trim($this->_chars));
                    $this->_targetURI = trim($this->_chars);
                }
            }
            break;
        }

        parent::endElement($uri, $element);
    }

    function characters($str)
    {
        if (isset($this->_currentSyncElement)) {
            $this->_currentSyncElement->characters($str);
        } else {
            if (isset($this->_chars)) {
                $this->_chars = $this->_chars . $str;
            } else {
                $this->_chars = $str;
            }
        }
    }

}
