<?php
/**
 * $Horde: chora/diff.php,v 1.87.8.5 2009/01/06 15:22:34 jan Exp $
 *
 * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Anil Madhavapeddy <avsm@horde.org>
 */

@define('CHORA_BASE', dirname(__FILE__));
require_once CHORA_BASE . '/lib/base.php';
require_once 'Horde/MIME/Magic.php';

/* Spawn the repository and file objects */
$fl = &$VC->getFileObject($where, $cache);
Chora::checkError($fl);

/* Initialise the form variables correctly. */
$r1 = Util::getFormData('r1');
$r2 = Util::getFormData('r2');

/* If we are in a SVN repository (or another VC system that doesn't
 * always use consecutive revision numbers from change-to-change
 * per-file) and we compare against an older version that does not
 * exist, then we search for the newest version older than the given
 * old version. */
if (!isset($fl->logs[$r1]) && isset($fl->logs[$r2]) && $r1 < $r2) {
    $rn = 0;
    foreach (array_keys($fl->logs) as $r) {
        if ($r > $rn && $r < $r1) {
            $rn = $r;
        }
    }
    if ($rn) {
        $r1 = $rn;
    }
}

/* Ensure that we have valid revision numbers. */
if (!VC_Revision::valid($r1) || !VC_Revision::valid($r2)) {
    Chora::fatal('403 Forbidden', _("Malformed Query"));
}

/* If no type has been specified, then default to human readable. */
$type = Util::getFormData('t', 'colored');
if (Util::getFormData('ty') == 'u') {
    $type = 'unified';
}

/* Unless otherwise specified, show whitespace differences and 3 lines
 * of context. */
$ws = Util::getFormData('ws', 1);
$num = (int)Util::getFormData('num', 3);

/* Cache the output of the diff for a week - it can be longer, since
 * it should never change. */
header('Cache-Control: max-age=604800');

/* All is ok, proceed with the diff. Always make sure there is a newline at
 * the end of the file - patch requires it. */
if ($type != 'colored') {
    header('Content-Type: text/plain');
    echo implode("\n", $VC->getDiff($fl, $r1, $r2, $type, $num, $ws)) . "\n";
    exit;
}

/* Human-Readable diff. */
$title = sprintf(_("Diff for %s between version %s and %s"),
                 Text::htmlallspaces($where), $r1, $r2);

/* Format log entries. */
$log = &$fl->logs;
$log_messages = array();
$range = Chora::getRevisionRange($r1, $r2);
foreach ($range as $val) {
    list($branchname, $branchrev) = Chora::getBranch($fl, $val);
    if (isset($log[$val])) {
        $clog = &$log[$val];
        $log_messages[] = array(
            'rev' => $val,
            'msg' => Chora::formatLogMessage($clog->queryLog()),
            'author' => Chora::showAuthorName($clog->queryAuthor(), true),
            'branchRev' => $branchrev,
            'branchName' => $branchname,
            'date' => Chora::formatDate($clog->queryDate()),
            'tags' => Chora::getTags($clog, $where),
        );
    }
}

Horde::addScriptFile('prototype.js', 'chora', true);
Horde::addScriptFile('stripe.js', 'chora', true);
require CHORA_TEMPLATES . '/common-header.inc';
require CHORA_TEMPLATES . '/menu.inc';
require CHORA_TEMPLATES . '/headerbar.inc';
require CHORA_TEMPLATES . '/diff/hr/header.inc';

$mime_type = MIME_Magic::filenameToMIME($fullname);
if (substr($mime_type, 0, 6) == 'image/') {
    /* Check for images. */
    $url1 = Chora::url('co', $where, array('r' => $r1, 'p' => 1));
    $url2 = Chora::url('co', $where, array('r' => $r2, 'p' => 1));

    echo "<tr><td><img src=\"$url1\" alt=\"" . htmlspecialchars($r1) . '" /></td>' .
        "<td><img src=\"$url2\" alt=\"" . htmlspecialchars($r2) . '" /></td></tr>';
} else {
    /* Retrieve the tree of changes. */
    $lns = VC_Diff::humanReadable($VC->getDiff($fl, $r1, $r2, 'unified', $num, $ws));
    if (!$lns) {
        /* Is the diff empty? */
        require CHORA_TEMPLATES . '/diff/hr/nochange.inc';
    } else {
        /* Iterate through every header block of changes. */
        foreach ($lns as $header) {
            $lefthead = $header['oldline'];
            $righthead = $header['newline'];
            require CHORA_TEMPLATES . '/diff/hr/row.inc';

            /* Each header block consists of a number of changes
             * (add, remove, change). */
            $curContext = '';
            foreach ($header['contents'] as $change) {
                if (!empty($curContext) && $change['type'] != 'empty') {
                    $line = $curContext;
                    $curContext = '';
                    require CHORA_TEMPLATES . '/diff/hr/empty.inc';
                }

                switch ($change['type']) {
                case 'add':
                    $line = '';
                    foreach ($change['lines'] as $l) {
                        $line .= htmlspecialchars($l) . '<br />';
                    }
                    require CHORA_TEMPLATES . '/diff/hr/add.inc';
                    break;

                case 'remove':
                    $line = '';
                    foreach ($change['lines'] as $l) {
                        $line .= htmlspecialchars($l) . '<br />';
                    }
                    require CHORA_TEMPLATES . '/diff/hr/remove.inc';
                    break;

                case 'empty':
                    $curContext .= htmlspecialchars($change['line']) . '<br />';
                    break;

                case 'change':
                    /* Pop the old/new stacks one by one, until both are
                     * empty. */
                    $oldsize = count($change['old']);
                    $newsize = count($change['new']);
                    $left = $right = '';
                    for ($row = 0, $rowMax = max($oldsize, $newsize); $row < $rowMax; ++$row) {
                        $left .= isset($change['old'][$row]) ? htmlspecialchars($change['old'][$row]) : '';
                        $left .= '<br />';
                        $right .= isset($change['new'][$row]) ? htmlspecialchars($change['new'][$row]) : '';
                        $right .= '<br />';
                    }
                    require CHORA_TEMPLATES . '/diff/hr/change.inc';
                    break;
                }
            }

            if (!empty($curContext)) {
                $line = $curContext;
                $curContext = '';
                require CHORA_TEMPLATES . '/diff/hr/empty.inc';
            }
        }
    }
}

require CHORA_TEMPLATES . '/diff/hr/footer.inc';
require $registry->get('templates', 'horde') . '/common-footer.inc';
