/*
 * Copyright (c) 2008 David Crawshaw <david@zentus.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

var diffview = function(diff) {
    var lines = diff.split('\n');
    var line = lines.shift();

    // find beginning of diff:  "--- <name of file>"
    while (line != null && line.indexOf("--- ") != 0)
        line = lines.shift();
    if (line == null)
        return null;

    // next line should be:     "+++ <name of file>"
    line = lines.shift();
    if (line.indexOf("+++ ") != 0)
        throw "diff parse error: expected +++";

    /*
     * From here on we should have chunks that begin with a line
     * marking segment of the form "@@ -i,j +k,l @@", where
     *      i: start line of the original file,
     *      j: lines after i in the original file,
     *      k: start line of the new file,
     *      l: lines after k in the new file.
     * Lines in the chunk begin with either '+', '-', or ' '.
     */
    var filter = function(text) {
        text = text.substring(1);
        if (text.length == 0 || (text.length == 1 && text.charAt(0) == ' '))
            return "&nbsp;"
        return text.replace(/&/g, "&amp;").replace(/</g, "&lt;");
    }
    var header = /^@@ -(\d+,?\d*) \+(\d+,?\d*) @@/;
    var text = "";
    var oldLines = "";
    var newLines = "";
    var lastchar = ' ';

    // process each cunk
    while ((line = lines.shift()) != null) {
        var chunk = header.exec(line);
        if (chunk == null)
            continue;
        var i, j, k, l;

        if (chunk[1].indexOf(',') == -1) {
            i = parseInt(chunk[1]);
            j = 1;
        } else {
            var vals = chunk[1].split(',');
            i = parseInt(vals[0]);
            j = parseInt(vals[1]);
        }
        if (chunk[2].indexOf(',') == -1) {
            k = parseInt(chunk[2]);
            l = 1;
        } else {
            var vals = chunk[2].split(',');
            k = parseInt(vals[0]);
            l = parseInt(vals[1]);
        }

        if (text != "") {
            // add a chunk divider
            if (lastchar == '+')
                textClassEx = 'DiffUntouchedAddMark';
            else if (lastchar == '-')
                textClassEx = 'DiffUntouchedDelMark';

            oldLines += '<pre class="DiffLineNum">...</pre>';
            newLines += '<pre class="DiffLineNum">...</pre>';
            text += '<pre class="DiffUntouched ' + textClassEx + '">...</pre>';

            lastchar = ' ';
        }

        // process the lines of a chunk
        while ((line = lines.shift()) != null) {
            if (line == "")
                continue;

            var textClass;
            var textClassEx = "";
            switch (line.charAt(0)) {
                case ' ':
                    j--; l--;
                    textClass = 'DiffUntouched';
                    if (lastchar == '+')
                        textClassEx = 'DiffUntouchedAddMark';
                    else if (lastchar == '-')
                        textClassEx = 'DiffUntouchedDelMark';
                    oldLines += '<pre class="DiffLineNum">' + i + '</pre>';
                    newLines += '<pre class="DiffLineNum">' + k + '</pre>';
                    i++; k++;
                    break;
                case '+':
                    l--;
                    textClass = 'DiffAdd';
                    if (lastchar == ' ') textClassEx = 'DiffAddMark';
                    oldLines += '<pre class="DiffLineNum">&nbsp;</pre>';
                    newLines += '<pre class="DiffLineNum">' + k + '</pre>';
                    k++;
                    break;
                case '-':
                    j--;
                    textClass = 'DiffDel';
                    if (lastchar == ' ') textClassEx = 'DiffDelMark';
                    oldLines += '<pre class="DiffLineNum">' + i + '</pre>';
                    newLines += '<pre class="DiffLineNum">&nbsp;</pre>';
                    i++;
                    break;
                case '\\':
                    // XXX what do we do with these silly "\ No newline..."?
                    continue;
                default:
                    throw "diff parse error: unexpected '" + line + "' in diff";
            }

            var wraps = Math.ceil(line.length / 80);

            text += '<pre class="' + textClass + ' ' + textClassEx + '">';
            text += filter(line.substring(0, 80));
            text += '</pre>\n';

            for (var z=1; z < wraps; z++) {
                text += '<pre class="' + textClass + '">';
                text += filter(line.substring(z * 80, (z * 80) + 80));
                text += '</pre>\n';
                oldLines += '<pre class="DiffLineNum">&nbsp;</pre>';
                newLines += '<pre class="DiffLineNum">&nbsp;</pre>';
            }

            lastchar = line.charAt(0);
            if (j == 0 && l == 0)
                break;
        }
        if (j != 0 || l != 0)
            throw "diff parse error: unexpected EOF";
    }

    if (text == "")
        return null;

    var table =
          '<table class="Diff" cellspacing="0" border="1">'
        + '<thead><tr>'
        + ' <th class="DiffNew">New</th>'
        + ' <th class="DiffOld">Old</th>'
        + ' <th>Changes</th>'
        + '</tr></thead>'
        + '<tbody><tr><td>'
        + newLines
        + '</td><td>'
        + oldLines
        + '</td><td>'
        + text
        + '</td></tr></tbody>'
        + '</table>';

    var div = document.createElement('div');
    div.innerHTML = table;
    return div;
}
