var xmlDoc;
var fontData = {};
var renderedGlyphs = {};
var callback_func = null;

var subdivisionsForCurves = 20; //constant
var scaleForFont = 0.1;

function fontMain(callback) {
    callback_func = callback;
    
    loadXMLAndWait();
    
}

function loadXMLAndWait() {
    loadXMLFont("arial.svg");
}

function loadXMLFont(path) {
    xmlDoc = document.implementation.createDocument("","",null);
    xmlDoc.load(path);
    xmlDoc.onload = xmlLoaded;
}

function xmlLoaded() {
    //4 means loaded, apparently
    var glyphs = xmlDoc.getElementsByTagName("glyph");
    
    for (var glyphX in glyphs) {
        if (glyphs[glyphX].attributes == undefined) continue;
    
        var glyphName = glyphs[glyphX].attributes.getNamedItem("unicode").nodeValue;

        var glyphDataNode = glyphs[glyphX].attributes.getNamedItem("d");
    
        //some glyphs are empty. hrmph.
        if (glyphDataNode != null) {        
            fontData[glyphName] = {};
            fontData[glyphName].d = glyphDataNode.nodeValue;
            
            var glyphWidthNode = glyphs[glyphX].attributes.getNamedItem("horiz-adv-x");
            fontData[glyphName].width = glyphWidthNode.nodeValue;
        }
    }
    
    //font all loaded! hooray!
    
    for (glyph in fontData) {
        renderedGlyphs[glyph] = renderPtsForChar(glyph);
    }
    
 //   beginDraw();
 
    callback_func();
}

function beginDraw() {
    var canvas = document.getElementById('fontcanvas');
    var ctx = null;

    if (ctx = canvas.getContext('2d'))
    {
        ctx.fillStyle = "rgb(0,255,0)";
        
        ctx.fillRect (0,0,25,25);
    }
    
    var r = parseFontOpcodes(fontData['k']);
    var tmpstr = "";

    r = transform (r, function (x,y) {  return [x*scaleForFont,y*scaleForFont+150] } );
    drawPath (canvas, r);
    
//    document.write (r.join(','));
}

function getPtsForChar(fontChar) {
    //return parseFontOpcodes(fontData[fontChar]);
    
    return renderedGlyphs[fontChar];
}

function renderPtsForChar(fontChar) {
    return parseFontOpcodes(fontData[fontChar].d);
}

function transform(pts, func) {
    return pts.map (function (pt) { if (pt.length == 2) return func(pt[0],pt[1]); else return pt;} );
}

function scale(pts,scalar) {
    return pts.map (
        function(pt) 
        { 
            if (pt.length == 2)
                return [pt[0]*scalar,pt[1]*scalar];
            else
                return pt;
            
        });
}

function drawPath(ctx, path) {
    ctx.strokeStyle = "rgb(0,255,0)";
    
    ctx.beginPath();
    
    var hasMovedTo = false;
    
    for (var pt in path) {
        if (path[pt].length == 2) {

            //pass in x and y
        
            if (hasMovedTo)
                ctx.lineTo(path[pt][0],path[pt][1]);
            else
                ctx.moveTo(path[pt][0],path[pt][1]);
            
            hasMovedTo = true;
        }
        else {
            //pick up pen
//            ctx.closePath();            
 //           ctx.stroke();

            hasMovedTo = false;
        }
        
    }
    
    ctx.closePath();            
    ctx.stroke();
}

function doBasicTransform (pts) {
//    return transform (pts, function (x,y) { return [x*0.2,-y*0.2+150] } ); //acknowledge
    return transform (pts, function (x,y) { return [x*scaleForFont,-y*scaleForFont+150] } ); //arial
}

function getPtsForWord (word) {
    var wordPts = [];
    var currentWidth = 0;

    for (var stringX = 0; stringX < word.length; stringX++) {
        var thisChar = word.charAt(stringX);
        var letterPts = getPtsForChar(thisChar);
        
        //space char has no points.
        if (letterPts == undefined) continue;
        
        letterPts = doBasicTransform (letterPts);
        letterPts = transform (letterPts, function (x,y) { return [x + currentWidth, y] } );
        
        wordPts = wordPts.concat (letterPts);
        
        currentWidth += (fontData[thisChar].width * scaleForFont) + 30;
    }
    
    return wordPts;
}

function parseFontOpcodes(fontData) {
    var svgNumberTokens = fontData.split (/[MLZHVCSQT ]{1}/);
    var svgOpcodeTokens = fontData.split (/[0-9 -]{1,}/);
    
    //oh javascript types, you are the bane of my existance.
    var svgNumbers = svgNumberTokens.filter (function (numToken) { return numToken != ''; } ).map
        (function (svgNum) { return Number(svgNum); } );
    
    svgOpcodeTokens = svgOpcodeTokens.map (function (str) {return str.replace(/^\s+|\s+$/g, '') ; } );
    
    //containers [x,y] pairs of coordinates, or 0 to pick up the pen
    var pointList = [];
    
    var firstX = 0;
    var firstY = 0;

    var x = 0;
    var y = 0;

    var lastCX = 0;
    var lastCY = 0;
    
    //index into svgNumbers
    var currentNumberToken = 0;
    
    for (var opX in svgOpcodeTokens) {
      
        //first, special cases...
        if (svgOpcodeTokens[opX] == "")
            continue;
        
        if (svgOpcodeTokens[opX] == "Z")
        {
            if (firstX != x || firstY != y)
            {
                pointList.push ([x,y]);
                pointList.push ([0]);
            }

            continue;
        }

        if (svgOpcodeTokens[opX] == "ZM")
        {
            if (firstX != x || firstY != y)
                pointList.push ([x,y]);
                
            pointList.push ([0]);
        }

        if (svgOpcodeTokens[opX] == "ZM" || svgOpcodeTokens[opX] == "M")
        {
            firstX = x = svgNumbers[currentNumberToken];

            firstY = y = svgNumbers[currentNumberToken+1];

            currentNumberToken+= 2;

            pointList.push ([0]);
            pointList.push ([x,y]);
            continue;
        }

        switch (svgOpcodeTokens[opX].charAt(0))
        {
            case 'L':
                x = svgNumbers[currentNumberToken];
                y = svgNumbers[currentNumberToken+1];

                //subdivide straight lines into 20 sections
                    
                currentNumberToken += 2;
    
                pointList.push ([x,y]);
            
                firstX = x; firstY = y;
    
                break;
    
            case 'H':
                x = svgNumbers[currentNumberToken++];
                
                pointList.push ([x,y]);
                
                firstX = x; firstY = y;
                
                break;
    
            case 'V':
                y = svgNumbers[currentNumberToken++];
                
                pointList.push ([x,y]);
                
                firstX = x; firstY = y;
                
                break;
    
            case 'Q':
    
                var newQPoints = genBezierCurve(x, y,
                        svgNumbers[currentNumberToken],
                        svgNumbers[currentNumberToken+1],
                        svgNumbers[currentNumberToken+2],
                        svgNumbers[currentNumberToken+3], subdivisionsForCurves);
    
                lastCX = svgNumbers[currentNumberToken];
                lastCY = svgNumbers[currentNumberToken+1];
    
                x = svgNumbers[currentNumberToken+2];
                y = svgNumbers[currentNumberToken+3];
    
                firstX = x; firstY = y;
    
                currentNumberToken += 4;
    
                pointList = pointList.concat (newQPoints);
    
                break;
    
            case 'T':
    
              var newTPoints = genBezierCurve(x, y,
                    x + (x - lastCX),
                    y + (y - lastCY),
                    svgNumbers[currentNumberToken],
                    svgNumbers[currentNumberToken+1], subdivisionsForCurves);
    
            lastCX = svgNumbers[currentNumberToken];
            lastCY = svgNumbers[currentNumberToken+1];
    
            x = svgNumbers[currentNumberToken];
            y = svgNumbers[currentNumberToken+1];
        
            currentNumberToken += 2;
    
            pointList = pointList.concat (newTPoints);
    
            break;
           
           default: 
            alert ('this should never happen!');
            break;
        }
    }

    pointList.push ([0]);
    
    return pointList;
}

function genBezierCurve(x0, y0, x1, y1, x2, y2, subdivisions) {
    var bezierPts = [];
    
    for (var x = 0; x < subdivisions; x++)
    {
        var tInCurve = x/subdivisions; //t

        //calculate x and y coords for B(t)

        var xCoord =  ( (Math.pow(1-tInCurve,2)* x0) + (2*tInCurve*(1-tInCurve)*x1 ) + (Math.pow (tInCurve,2) * x2));
        var yCoord =  ( (Math.pow(1-tInCurve,2)* y0) + (2*tInCurve*(1-tInCurve)*y1 ) + (Math.pow (tInCurve,2) * y2));

        //...and store them in an array!
        bezierPts[x] = [xCoord,yCoord];
    }
    
    return bezierPts;
}