// $Id: frames.js 163 2010-08-10 01:34:45Z victor $
/*
 var explodingHeartDesc = [ // array of 'frames'
    
    [   // array of 'lines'
        { c:  'red', a: 1.0, f: null, w: 1, t: 3, 
            p: [ -10, 0,
                 1,   2, 2, 4, 4, 5,
                -93, 88, 0, 8, 0, -1 ] },
        { c:  'red', a: 1.0, f: null, w: 1, t: 3, 
            p: [ -10, 0,
                 1,   2, 2, 4, 4, 5,
                -93, 88, 0, 8, 0, -1 ] },
    ],
    [   
        { c:  'red', a: 1.0, f: null, w: 1, t: 3, 
            p: [ -10, 0,
                 1,   2, 2, 4, 4, 5,
                -93, 88, 0, 8, 0, -1 ] },
        { c:  'red', a: 1.0, f: null, w: 1, t: 3, 
            p: [ -10, 0,
                 1,   2, 2, 4, 4, 5,
                -93, 88, 0, 8, 0, -1 ] },
    ],
    
    BPF.explodingHeart = new tooDeeLineFrames( 12, explodingHeartDesc )
 ]
*/

function tooDeeLineFrames(fps,frames,colorDict)
{
    this.fps = fps;
    this.frames = frames || [[]];
    this.handleSize = 0;
    this.position = { x: 0, y: 0 }; // in global coords
    
    var Fs = this.frames,
        len = Fs.length,
        F, fLen, L;
        
    if( colorDict )
    {
        for( var i = 0; i < len; i++ )
        {
            F = Fs[i];
            fLen = F.length;
            for( var n = 0; n < fLen; n++ )
            {
                L = F[n];
                L.c = colorDict[L.c];
                L.f = colorDict[L.f];
            }
        }        
    }    
};


tooDeeLineFrames.prototype.Draw = function(ctx,desc,handles)
{
    ctx.save();
    ctx.translate( this.position.x, this.position.y );
    var F = this.frames[desc.frame], len = F.length, selLine = desc.line;
    for( var i = 0; i < len; i++ )
        this.DrawLine(ctx,F[i], handles && (i == selLine) );
    ctx.restore();
};

tooDeeLineFrames.prototype.DrawLine = function(ctx,line,handles)
{
    var P = line.p,
        segSize = line.t * 2;
        numSegs = (P.length-2) / segSize,
        i = 0,
        n = 2;
    ctx.save();
    ctx.lineWidth = line.w;
    ctx.beginPath();
    ctx.moveTo(P[0],P[1]);
    switch( line.t )
    {
        case 3: // bezier
            for( ; i < numSegs; i++, n+=segSize )
            {
                ctx.bezierCurveTo( P[n], P[n+1],
                                   P[n+2], P[n+3],
                                   P[n+4], P[n+5] );
            }
            break;
        case 2: // quadratic
            for( ; i < numSegs; i++, n+=segSize )
            {
                ctx.quadraticCurveTo(   P[n], P[n+1],
                                        P[n+2], P[n+3] );
            }
            break;
        case 1: // line
            for( ; i < numSegs; i++, n+=segSize )
            {
                ctx.lineTo( P[n], P[n+1] );
            }
            break;
    }
    
    var alpha = (line.a && parseFloat(line.a)) || 1.0;
    
    if( alpha != 1.0 )
        ctx.globalAlpha = alpha;
        
    if( line.f )
    {
        ctx.fillStyle = line.f;
        ctx.fill();
    }
    
    if( line.c )
    {
        ctx.strokeStyle = line.c;
        ctx.stroke();
    }

    if( alpha != 1.0 )
        ctx.globalAlpha = 1.0;

    if( handles && this.handleSize )
    {
        ctx.strokeStyle = '#999';
        var handleSize = this.handleSize;
        var ex = handleSize / 2;
        for( i = 0; i < P.length; i += 2 )
        {
            var p1 = P[i], p2 = P[i+1];
            ctx.strokeRect( p1-ex, p2-ex, handleSize, handleSize );
        }
    }
    ctx.restore();
};

tooDeeLineFrames.prototype.NumFrames = function()
{
    return this.frames.length;
};

tooDeeLineFrames.prototype.CalculateBounds = function(justThisFrame)
{    
    if( !this.bounds )
    {
        this.bounds = [];
        var Fs = this.frames,
            len = Fs.length,
            F, fLen, L;
        var minX, minY, maxX, maxY;
        for( var i = 0; i < len; i++ )
        {
            F = Fs[i];
            fLen = F.length;
            minX = minY = Number.MAX_VALUE;
            maxX = maxY = -Number.MAX_VALUE;
            for( var n = 0; n < fLen; n++ )
            {
                var P = F[n].p
                for( var p = 0; p < P.length; p += 2 )
                {
                    if( P[p] < minX )
                        minX = P[p];
                    if( P[p] > maxX )
                        maxX = P[p];
                    if( P[p+1] < minY )
                        minY = P[p+1];
                    if( P[p+1] > maxY )
                        maxY = P[p+1];
                }
            }
            
            this.bounds[i] = new tooDeeDimensions().Set( minX, minY, maxX, maxY );
        }
    }
    
    var retBounds = [], start, num;
    
    if( justThisFrame !== undefined )
    {
        start = justThisFrame;
        num = 1;
    }
    else
    {
        start = 0;
        num = this.bounds.length;
    }
    
    for( var i = start; i < num; i++ )
    {
        retBounds[i] = this.bounds[i].Copy().Offset( this.position );
    }
    
    return retBounds;
};

//////////////////////////////////////////
//
//   Editor function below...
//

tooDeeLineFrames.prototype.toString = function()
{
    var str = '';

    var Fs = this.frames, len = Fs.length, comma = '\n', i, n, F, fLen,
        comma, fComma, L, fill, color, colorDict = [],
        colorIndecies = {}, colorIndex = 1;
    
    colorDict.push(null);
    
    for( i = 0; i < len; i++ )
    {
        F = Fs[i];
        fLen = F.length;
        for( n = 0; n < fLen; n++ )
        {
            L = F[n];
            if( L.c && !colorIndecies[L.c] )
            {
                colorDict[colorIndex-1] = L.c;
                colorIndecies[L.c] = colorIndex++;
            }
            if( L.f && !colorIndecies[L.f] )
            {
                colorDict[colorIndex-1] = L.f;
                colorIndecies[L.f] = colorIndex++;
            }
        }
    }

    var colorDictStr  = "[ null, '" + colorDict.join("','") + "']";
    
    str += 'new tooDeeLineFrames( ' + this.fps + ', [';
    
    for( i = 0; i < len; i++ )
    {
        str += comma;
        str += ' ['
        
        F = Fs[i];
        fLen = F.length;
        fComma = '';
        for( n = 0; n < fLen; n++ )
        {
            L = F[n];
            fill = L.f ? colorIndecies[L.f] : 0;
            color = L.c ? colorIndecies[L.c] : 0;
            str += fComma
                + '{ c: ' + color + ', a: ' + L.a + ', w: ' + L.w  + ', f: ' + fill + ','
                + ' t: ' + L.t + ','
                + ' p: [ ' + L.p.join(',') + '] '
                + '}';

            fComma = ',\n  ';
        }
        
        str += ']';
        comma = ',\n';
    }
    
    str += '],\n' + colorDictStr + ');'
    
    return str;
};

tooDeeLineFrames.prototype.PointInHandle = function(desc,v)
{
    var F = this.frames[desc.frame],len = F.length, ex = this.handleSize / 2;

    for( var i = 0; i < len; i++ )
    {
        var P = F[i].p;
        for( var n = 0; n < P.length; n += 2 )
        {
            var ptx = P[n],    pty = P[n+1],
                x1 = ptx - ex, x2 = ptx + ex,
                y1 = pty - ex, y2 = pty + ex;
                
            if( (v.x > x1) &&
                (v.x < x2) &&
                (v.y > y1) &&
                (v.y < y2) )
            {
                desc.line = i;
                desc.pt = n;
                desc.ptxy = { x: ptx, y: pty };
                return desc;
            }
            
        }
    }
    
    return null;
}

tooDeeLineFrames.prototype.GetPoint = function( desc )
{
    var P = this.frames[ desc.frame ][ desc.line ].p;
    desc.ptxy = { x: P[ desc.pt ], y: P[ desc.pt + 1 ] };
}

tooDeeLineFrames.prototype.UpdatePoint = function( desc )
{
    var P = this.frames[ desc.frame ][ desc.line ].p;
    P[ desc.pt ] = desc.ptxy.x;
    P[ desc.pt + 1 ] = desc.ptxy.y;
};

tooDeeLineFrames.prototype.AddFrame = function(line)
{
    this.frames.push([]);
    var frameNum = this.frames.length - 1;
    return this.AddLine(frameNum,line);
};

tooDeeLineFrames.prototype.ReplaceFrame = function(frameNum,frame)
{
    this.frames[frameNum] = frame;
}

tooDeeLineFrames.prototype.InsertFrame = function(at,frame)
{
    this.frames.splice(at,0,frame);
}

tooDeeLineFrames.prototype.DeleteFrame = function(frameNum)
{
    this.frames.splice(frameNum,1);
};

tooDeeLineFrames.prototype.AddClonedFrame = function(frame)
{
    this.frames.push(frame);
    var frameNum = this.frames.length - 1, line = frame[0];
    return { frame: frameNum, line: 0, pt: 0, ptxy: { x: line.p[0], y: line.p[1] } };
};

tooDeeLineFrames.prototype.AddLine = function(frameNum,line)
{
    var F = this.frames[frameNum];
    F.push(line);
    return { frame: frameNum, line: F.length - 1, pt: 0, ptxy: { x: line.p[0], y: line.p[1] } };
};

tooDeeLineFrames.prototype.RemoveLine = function(frameNum,lineNum)
{    
    this.frames[frameNum].splice(lineNum,1);
    return this.frames[frameNum].length;
}

tooDeeLineFrames.prototype.MoveLineBy = function(desc,x,y)
{
    var P = this.frames[ desc.frame ][ desc.line ].p;
    for( var i = 0; i < P.length; i += 2 )
    {
        P[i] += x;
        P[i+1] += y;
    }
    
}
tooDeeLineFrames.prototype.AddSegment = function(desc,x,y)
{
    var L = this.frames[ desc.frame ][ desc.line ], P = L.p,
        lastX = P[P.length - 2],
        lastY = P[P.length-1], at = P.length,
        extendX = x - lastX, extendY = y - lastY;
        
    for( var n = 1; n < (L.t+1); ++n )
    {
        P[at++] = lastX + (extendX * (n/L.t));
        P[at++] = lastY + (extendY * (n/L.t));
    }
}

