goog.provide('FlappSWFShapeRecords');
goog.require('FlappSWFFillStyles');

goog.scope(function() {

/**
 * @constructor
 */
FlappSWFShapeRecords = function(ibit) {
    ;
};

FlappSWFShapeRecords.loadAndMarkEdgeLoop = function(code, ibit, fillStyles, lineStyles, numFillBits, numLineBits) {
    var shapes = FlappSWFShapeRecords.load(code, ibit, fillStyles, lineStyles, numFillBits, numLineBits);
    var edges = FlappSWFShapeRecords.convertEdges(shapes);
    shapes = null ; // for GC
    FlappSWFShapeRecords.markFillEdgeLoop(edges.fillEdges);
    return edges;
}

FlappSWFShapeRecords.load = function(code, ibit, fillStyles, lineStyles, numFillBits, numLineBits) {
    var shapeRecords = []; // [[fillstyle0, fillstyle1, linestyle, edgetypes, [edges]], [], ...]
    var fillStyleBaseIndex = 0;
    var lineStyleBaseIndex = 0;
    var fillStyle0 = 0, fillStyle1 = 0, lineStyle = 0;
    var currentX = 0, currentY = 0;
    ibit.a();
    var edgeTypeArray = [];
    var edges = [currentX, currentY];
    var first6bits;
    var minX, minY, maxX, maxY;
    while (first6bits = ibit.ub(6)) {
        if (first6bits & 0x20) { // Edge (1XXXXX)
            var numBits = first6bits & 0x0F;
            if (first6bits & 0x10) { // Straight Edge (11XXXX)
                var deltaX = 0, deltaY = 0;
                var generalLine = ibit.b();
                if (generalLine) { // Straight Edge
                    deltaX = ibit.sb(numBits + 2);
                    deltaY = ibit.sb(numBits + 2);
                } else { // Straight Edge
                    var vertLine = ibit.b();
                    if (vertLine) { // Straight Edge
                        deltaY = ibit.sb(numBits + 2);
                    } else {
                        deltaX = ibit.sb(numBits + 2);
                    }
                }
                var x = currentX + deltaX;
                var y = currentY + deltaY;
                edgeTypeArray.push('S');
                edges.push(x, y);
                currentX = x;
                currentY = y;
                if (minX > x) minX = x;
                if (maxX < x) maxX = x;
                if (minY > y) minY = y;
                if (maxY < y) maxY = y;
            } else { // Curved Edge (10XXXX)
                var controlDeltaX = ibit.sb(numBits + 2);
                var controlDeltaY = ibit.sb(numBits + 2);
                var anchorDeltaX = ibit.sb(numBits + 2);
                var anchorDeltaY = ibit.sb(numBits + 2);
                var controlX = currentX + controlDeltaX;
                var controlY = currentY + controlDeltaY;
                var anchorX = controlX + anchorDeltaX;
                var anchorY = controlY + anchorDeltaY;
                edgeTypeArray.push('C');
                edges.push(controlX, controlY, anchorX, anchorY);
                currentX = anchorX;
                currentY = anchorY;
                if (minX > controlX) minX = controlX;
                if (maxX < controlX) maxX = controlX;
                if (minY > controlY) minY = controlY;
                if (maxY < controlY) maxY = controlY;
                if (minX > anchorX) minX = anchorX;
                if (maxX < anchorX) maxX = anchorX;
                if (minY > anchorY) minY = anchorY;
                if (maxY < anchorY) maxY = anchorY;
            }
        } else if (first6bits) { // Change (0XXXXX != 000000)
            if (edgeTypeArray.length > 0) {
                shapeRecords.push(
                    {fillStyle0:fillStyle0, fillStyle1:fillStyle1, lineStyle:lineStyle,
                     edgeTypes:edgeTypeArray.join(''),edges:edges,
                     minX:minX, maxX: maxX, minY:minY, maxY:maxY}
                );
            }
            var stateNewStyles  = first6bits & 0x10;
            var stateLineStyle  = first6bits &    8;
            var stateFillStyle1 = first6bits &    4;
            var stateFillStyle0 = first6bits &    2;
            var stateMoveTo     = first6bits &    1;

            if (stateMoveTo) {
                var moveBits = ibit.ub(5);
                currentX = ibit.sb(moveBits); // MoveDeltaX(?)
                currentY = ibit.sb(moveBits); // MoveDeltaY(?)
            }
            if (stateFillStyle0) { // 
                fillStyle0 = ibit.ub(numFillBits);
            }
            if (stateFillStyle1) {
                fillStyle1 = ibit.ub(numFillBits);
            }
            if (stateLineStyle) {
                lineStyle = ibit.ub(numLineBits);
            }
            if (stateNewStyles) {
                fillStyleBaseIndex = fillStyles.length;
                lineStyleBaseIndex = lineStyles.length;
                var appendFillStyles = FlappSWFFillStyles.load(code, ibit);
                var appendLineStyles = FlappSWFLineStyles.load(code, ibit);
                for (var i = 0, l = appendFillStyles.length ; i < l ; i++) {
                    fillStyles.push(appendFillStyles[i]);
                }
                for (var i = 0, l = appendLineStyles.length ; i < l ; i++) {
                    lineStyles.push(appendLineStyles[i]);
                }
                ibit.a();
                var numBits = ibit.ui8();
                numFillBits = numBits >> 4;
                numLineBits = numBits & 0x0f;
            }
            if (stateFillStyle0 && fillStyle0) { 
                fillStyle0 += fillStyleBaseIndex;
            }
            if (stateFillStyle1 && fillStyle1) {
                fillStyle1 += fillStyleBaseIndex;
            }
            if (stateLineStyle && lineStyle) {
                lineStyle += lineStyleBaseIndex;
            }
            edgeTypeArray = [];
            edges = [currentX, currentY];
            minX = maxX = currentX;
            minY = maxY = currentY;
        }
    }
    if (edgeTypeArray.length > 0) {
        shapeRecords.push(
            {fillStyle0:fillStyle0, fillStyle1:fillStyle1, lineStyle:lineStyle,
             edgeTypes:edgeTypeArray.join(''), edges:edges,
             minX:minX, maxX: maxX, minY:minY, maxY:maxY});
    }
    return shapeRecords;
};

FlappSWFShapeRecords.convertEdges = function(shapeRecords) {
    var i, l;
    var fillEdges = {}; // fillStyle => []
    var lineEdges = {}; // lineStyle => []
    for (i = 0, l = shapeRecords.length ; i < l ; i++) {
        var record = shapeRecords[i];
        var i2, l2 = record.edges.length;
        var fillStyle0 = record.fillStyle0;
        var fillStyle1 = record.fillStyle1;
        var lineStyle = record.lineStyle;
        var edgeTypes = record.edgeTypes;
        var edges = record.edges;
        var edgesArray;
        if (fillStyle0) {
            edgesArray = new Int32Array(l2);
            for (i2 = 0 ; i2 < l2 ; i2++) {
                edgesArray[i2] = edges[i2];
            }
            if (! (fillStyle0 in fillEdges)) {
                fillEdges[fillStyle0] = [];
            }
            fillEdges[fillStyle0].push({types:edgeTypes,
                                        edges:edgesArray,
                                        prev:null, next:null,
                                        minX:record.minX, maxX:record.maxX,
                                               minY:record.minY, maxY:record.maxY});
        }
        if (fillStyle1) {
            var rl2 = l2; // reverse l2
            edgesArray = new Int32Array(l2);
            for (i2 = 0 ; i2 < l2 ; i2 += 2) {
                edgesArray[i2+1] = edges[--rl2];
                edgesArray[i2] = edges[--rl2];
            }
            if (! (fillStyle1 in fillEdges)) {
                fillEdges[fillStyle1] = [];
            }
            fillEdges[fillStyle1].push({types:edgeTypes.split('').reverse().join(''),
                                        edges:edgesArray,
                                        prev:null, next:null,
                                        minX:record.minX, maxX:record.maxX,
                                               minY:record.minY, maxY:record.maxY});
        }
        if (lineStyle) {
            edgesArray = new Int32Array(l2);
            for (i2 = 0 ; i2 < l2 ; i2++) {
                edgesArray[i2] = edges[i2];
            }
            if (! (lineStyle in fillEdges)) {
                fillEdges[lineStyle] = [];
            }
            lineEdges[lineStyle].push({types:edgeTypes,
                                       edges:edgesArray,
                                       minX:record.minX, maxX:record.maxX,
                                       minY:record.minY, maxY:record.maxY});
        }
    }
    // max, min, X, Y
    var minX, maxX, minY, maxY;
    var minX2, maxX2, minY2, maxY2;
    var edgesInfoList, edgesInfo0, edgesInfo;
    for (var style in fillEdges) {
        edgesInfoList = fillEdges[style];
        edgesInfo0 = edgesInfoList[0];
        minX = edgesInfo0.minX;
        maxX = edgesInfo0.maxX;
        minY = edgesInfo0.minY;
        maxY = edgesInfo0.maxY;
        for (i = 0, l = edgesInfoList.length ; i < l ; i++) {
            edgesInfo = edgesInfoList[i];
            minX2 = edgesInfo.minX;
            maxX2 = edgesInfo.maxX;
            minY2 = edgesInfo.minY;
            maxY2 = edgesInfo.maxY;
            if (minX > minX2) minX = minX2;
            if (maxX < maxX2) maxX = maxX2;
            if (minY > minY2) minX = minY2;
            if (maxY < maxY2) maxX = maxY2;
        }
        edgesInfoList.minX = minX;
        edgesInfoList.maxX = maxX;
        edgesInfoList.minY = minY;
        edgesInfoList.maxY = maxY;
    }
    for (var style in lineEdges) {
        edgesInfoList = lineEdges[style];
        edgesInfo0 = edgesInfoList[0];
        minX = edgesInfo0.minX;
        maxX = edgesInfo0.maxX;
        minY = edgesInfo0.minY;
        maxY = edgesInfo0.maxY;
        for (i = 0, l = edgesInfoList.length ; i < l ; i++) {
            edgesInfo = edgesInfoList[i];
            minX2 = edgesInfo.minX;
            maxX2 = edgesInfo.maxX;
            minY2 = edgesInfo.minY;
            maxY2 = edgesInfo.maxY;
            if (minX > minX2) minX = minX2;
            if (maxX < maxX2) maxX = maxX2;
            if (minY > minY2) minX = minY2;
            if (maxY < maxY2) maxX = maxY2;
        }        
        edgesInfoList.minX = minX;
        edgesInfoList.maxX = maxX;
        edgesInfoList.minY = minY;
        edgesInfoList.maxY = maxY;
    }
    return {fillEdges:fillEdges, lineEdges:lineEdges};
}

FlappSWFShapeRecords.markFillEdgeLoop = function(fillEdges) {
    var style, edgesInfoList;
    var i, l, i2, edgesInfo, edges;
    for (style in fillEdges) {
        edgesInfoList = fillEdges[style];
        for (i = 0, l = edgesInfoList.length ; i < l ; i++) {
            edgesInfo = edgesInfoList[i];
            if (edgesInfo.next !== null) {
                continue; // skip
            }
            edges = edgesInfo.edges;
            var edgesLastX = edges[edges.length - 2];
            var edgesLastY = edges[edges.length - 1];
            // self loop check
            if ((edgesLastX === edges[0]) && (edgesLastY === edges[1])) {
                edgesInfo.next = edgesInfo.prev = i;
                continue;
            }
            for (i2 = 0; i2 < l ; i2++) {
                var targetEdgesInfo = edgesInfoList[i2];
                if (targetEdgesInfo.prev !== null) {
                    continue;
                }
                var targetEdges = targetEdgesInfo.edges;
                if ((edgesLastX === targetEdges[0]) &&
                    (edgesLastY === targetEdges[1])){
                    edgesInfo.next = i2;
                    targetEdgesInfo.prev = i;
                    break;
                }
            }
            if (i2 === l) {
                console.warn("edges link not found");
            }
        }
    }
//    console.debug('fillEdges');
//    console.debug(fillEdges);
}

});
