var svgCache;
var svgDefs;
var svgGradients;
var svgNS = "http://www.w3.org/2000/svg";
var svgRoot;

function displaySvg(displayList) {
    for (var index = 0; index < displayList.length; ++index) {
        drawToSvg(displayList[index]);
    }
}

function drawToSvg(display) {
    assert('string' == typeof(display.ref));
    var cache;
    if (display.ref in svgCache) {
        cache = svgCache[display.ref];
        if (display.drawDirty) {
            switch (cache.spec) {
                case "paths":
                    svgSetPathData(cache.element, display.draw);
                    break;
                case "pictures":
                    svgSetPictureData(cache.element, display.draw);
                    break;
                case "text":
                    svgCreateText(cache.element, display.draw);
                    break;
                default:
                    assert(0);
            }
        }
    } else {
        cache = {};
        cache.action = display;
        cache.spec = display.drawSpec;
        var dot = cache.spec.indexOf(".");
        if (dot > 0) {
            cache.spec = cache.spec.substring(0, dot);
        }
        switch (cache.spec) {
            case "paths":
                cache.element = svgCreatePath(display.ref, display.draw);
                break;
            case "pictures":
                cache.element = svgCreatePicture(display.ref, display.draw);
                break;
            case "text":
                cache.element = svgCreateText(display.ref, display.draw);
                break;
            default:
                assert(0);
        }
    }
    display.drawDirty = false;
    if (display.paintDirty) {
        svgSetPaintData(cache.element, display.paint);
        var opacity = svg_opacity(display.paint.color);
        cache.element.setAttribute("fill-opacity", opacity);
        cache.element.setAttribute("stroke-opacity", opacity);
        display.paintDirty = false;
    }
    assert('object' == typeof(cache));
    if (!(display.ref in svgCache)) {
        svgRoot.appendChild(cache.element);
        svgCache[display.ref] = cache;
    }
}

function setupSvg() {
    svgCache = { "paths":{}, "pictures":{}, "text":{} };
    svgDefs = document.createElementNS(svgNS, "defs");
    svgGradients = {};
    svgRoot = document.getElementById("svg");
    while (svgRoot.lastChild) {
        svgRoot.removeChild(svgRoot.lastChild);
    }
    svgRoot.appendChild(svgDefs);
}

function svg_rbg(color) {
    return "rgb(" + ((color >> 16) & 0xFF)
            + "," + ((color >>  8) & 0xFF)
            + "," + ((color >>  0) & 0xFF) + ")";
}

function svg_opacity(color) {
    return ((color >> 24) & 0xFF) / 255.0;
}

function svgCreatePath(key, path) {
    var svgPath = document.createElementNS(svgNS, "path");
    svgPath.setAttribute("id", key);
    svgSetPathData(svgPath, path);
    return svgPath;
}

function svgCreatePicture(key, picture) {
    var svgPicture = document.createElementNS(svgNS, "g");
    svgPicture.setAttribute("id", key);
    svgSetPictureData(svgPicture, picture);
    return svgPicture;
}

function svgCreateRadialGradient(key) {
    var g = gradients[key];
    var e = document.createElementNS(svgNS, "radialGradient");
    e.setAttribute("id", key);
    e.setAttribute("cx", g.cx);
    e.setAttribute("cy", g.cy);
    e.setAttribute("r", g.r);
    e.setAttribute("gradientUnits", "userSpaceOnUse");
    var stopLen = g.stops.length;
    for (var index = 0; index < stopLen; ++index) {
        var stop = g.stops[index];
        var color = svg_rbg(stop.color);
        var s = document.createElementNS(svgNS, 'stop');
        s.setAttribute("offset", stop.offset);
        var style = "stop-color:" + svg_rbg(stop.color) + "; stop-opacity:"
                + svg_opacity(stop.color);
        s.setAttribute("style", style);
        e.appendChild(s);
    }
    svgGradients[key] = e;
    svgDefs.appendChild(e);
}

function svgCreateText(key, text) {
    var svgText = document.createElementNS(svgNS, "text");
    svgText.setAttribute("id", key);
    var textNode = document.createTextNode(text.string);
    svgText.appendChild(textNode);
    svgSetTextData(svgText, text);
    return svgText;
}

function svgSetPathData(svgPath, path) {
    var dString = "";
    for (var cIndex = 0; cIndex < path.length; ++cIndex) {
        var curveKey = Object.keys(path[cIndex])[0];
        var v = path[cIndex][curveKey];
        switch (curveKey) {
            case 'arcTo':
                var clockwise = 1; // to do; work in general case
                dString += " A" + v[4] + "," + v[4] + " 0 0," + clockwise + " "
                        + v[2] + "," + v[3];
                break;
            case 'close':
                dString += " z";
                break;
            case 'cubic':
                dString += " M" + v[0] + "," + v[1];
                dString += " C" + v[2] + "," + v[3]
                          + " " + v[4] + "," + v[5]
                          + " " + v[6] + "," + v[7];
                break;
            case 'line':
                dString += " M" + v[0] + "," + v[1];
                dString += " L" + v[2] + "," + v[3];
                break;
            case 'quad':
                dString += " M" + v[0] + "," + v[1];
                dString += " Q" + v[2] + "," + v[3]
                          + " " + v[4] + "," + v[5];
                break;
            default:
                assert(0);
        }
    }
    svgPath.setAttribute("d", dString);
}

function svgSetPaintData(svgElement, paint) {
    var color;
    var inPicture = 'string' == typeof(paint);
    if (inPicture) {
        paint = (new Function("return " + paint))();
        assert('object' == typeof(paint) && !isArray(paint));
    }
    if ('gradient' in paint) {
        var gradient = paint.gradient.split('.');
        var gradName = gradient[1];
        if (!svgGradients[gradName]) {
            svgCreateRadialGradient(gradName);
        }
        color = "url(#" + gradName + ")";
    } else {
        color = svg_rbg(paint.color);
    }
    svgElement.setAttribute("fill", 'fill' == paint.style ? color : "none");
    if ('stroke' == paint.style) {
        svgElement.setAttribute("stroke", color);
    }
    if ('strokeWidth' in paint) {
        svgElement.setAttribute("stroke-width", paint.strokeWidth);
    }
    if ('typeface' in paint) {
        var typeface = typefaces[paint.typeface];
        var font = typeface.style;
        if ('textSize' in paint) {
            svgElement.setAttribute("font-size", paint.textSize);
        }
        if ('family' in typeface) {
            svgElement.setAttribute("font-family", typeface.family);
        }
        if ('textAlign' in paint) {
            svgElement.setAttribute("text-anchor", paint.textAlign == "right" ? "end" : assert(0));
        }
        if ('textBaseline' in paint) {
            svgElement.setAttribute("alignment-baseline", paint.textBaseline);
        }
    }
}

function svgSetPictureData(svgPicture, picture) {
    while (svgPicture.lastChild) {
        svgPicture.removeChild(svgPicture.lastChild);
    }
    for (var index = 0; index < picture.length; ++index) {
        var entry = picture[index];
        var drawObj = (new Function("return " + entry.draw))();
        var drawSpec = entry.draw.split('.');
        var svgElement;
        switch (drawSpec[0]) {
            case 'paths':
                svgElement = svgCreatePath(drawSpec[1], drawObj);
                break;
            case 'pictures':
                svgElement = svgCreatePicture(drawSpec[1], drawObj);
                break;
            case 'text':
                svgElement = svgCreateText(drawSpec[1], drawObj);
                break;
            default:
                assert(0);
        }
        var paintObj = (new Function("return " + entry.paint))();
        svgSetPaintData(svgElement, paintObj);
        svgPicture.appendChild(svgElement);
    }
}

function svgSetTextData(svgElement, text) {
    svgElement.setAttribute('x', text.x);
    svgElement.setAttribute('y', text.y);
}