/*jslint vars: true , plusplus: true, continue:true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global bm_keyframeHelper, bm_eventDispatcher, bm_generalUtils, PropertyFactory, Matrix*/
var bm_shapeHelper = (function () {
    'use strict';
    var ob = {}, shapeItemTypes = {
        shape: 'sh',
        rect: 'rc',
        ellipse: 'el',
        star: 'sr',
        fill: 'fl',
        gfill: 'gf',
        gStroke: 'sf',
        stroke: 'st',
        merge: 'mm',
        trim: 'tm',
        group: 'gr',
        roundedCorners: 'rd'
    };
    var navigationShapeTree = [];

    function getItemType(matchName) {
        switch (matchName) {
        case 'ADBE Vector Shape - Group':
            return shapeItemTypes.shape;
        case 'ADBE Vector Shape - Star':
            return shapeItemTypes.star;
        case 'ADBE Vector Shape - Rect':
            return shapeItemTypes.rect;
        case 'ADBE Vector Shape - Ellipse':
            return shapeItemTypes.ellipse;
        case 'ADBE Vector Graphic - Fill':
            return shapeItemTypes.fill;
        case 'ADBE Vector Graphic - Stroke':
            return shapeItemTypes.stroke;
        case 'ADBE Vector Graphic - Merge':
        case 'ADBE Vector Filter - Merge':
            return shapeItemTypes.merge;
        case 'ADBE Vector Graphic - Trim':
        case 'ADBE Vector Filter - Trim':
            return shapeItemTypes.trim;
        case 'ADBE Vector Filter - RC':
            return shapeItemTypes.roundedCorners;
        case 'ADBE Vector Group':
            return shapeItemTypes.group;
        case 'ADBE Vector Graphic - G-Fill':
            return shapeItemTypes.gfill;
        case 'ADBE Vector Graphic - G-Stroke':
            return shapeItemTypes.gStroke;
        default:
            bm_eventDispatcher.log(matchName);
            return '';
        }
    }
    
    function reverseShape(ks, isClosed) {
        var newI = [], newO = [], newV = [];
        var i, len;
        if (ks.i) {
            var init = 0;
            if (isClosed) {
                newI[0] = ks.o[0];
                newO[0] = ks.i[0];
                newV[0] = ks.v[0];
                init = 1;
            }
            len = ks.i.length;
            var cnt = len - 1;
            
            for (i = init; i < len; i += 1) {
                newI.push(ks.o[cnt]);
                newO.push(ks.i[cnt]);
                newV.push(ks.v[cnt]);
                cnt -= 1;
            }
            
            ks.i = newI;
            ks.o = newO;
            ks.v = newV;
        } else {
            len = ks.length;
            for (i = 0; i < len - 1; i += 1) {
                reverseShape(ks[i].s[0], isClosed);
                reverseShape(ks[i].e[0], isClosed);
            }
        }
    }
    
    function iterateProperties(iteratable, array, frameRate, isText) {
        var i, len = iteratable.numProperties, ob, prop, itemType;
        for (i = 0; i < len; i += 1) {
            prop = iteratable.property(i + 1);
            if (prop.enabled) {
                itemType = getItemType(prop.matchName);
                if (isText && itemType !== shapeItemTypes.shape && itemType !== shapeItemTypes.group && itemType !== shapeItemTypes.merge) {
                    continue;
                }
                if (itemType === shapeItemTypes.shape) {
                    ob = {};
                    ob.ind = i;
                    ob.ty = itemType;
                    ob.closed = prop.property('Path').value.closed;
                    ob.ks = bm_keyframeHelper.exportKeyframes(prop.property('Path'), frameRate);
                    if (prop.property("Shape Direction").value === 3) {
                        reverseShape(ob.ks.k, ob.closed);
                    }
                    //bm_generalUtils.convertPathsToAbsoluteValues(ob.ks.k);
                } else if (itemType === shapeItemTypes.rect && !isText) {
                    ob = {};
                    ob.ty = itemType;
                    ob.d = prop.property("Shape Direction").value;
                    ob.s = bm_keyframeHelper.exportKeyframes(prop.property('Size'), frameRate);
                    ob.p = bm_keyframeHelper.exportKeyframes(prop.property('Position'), frameRate);
                    ob.r = bm_keyframeHelper.exportKeyframes(prop.property('Roundness'), frameRate);
                } else if(itemType === shapeItemTypes.star && !isText) {
                    ob = {};
                    bm_generalUtils.iterateProperty(prop);
                    ob.ty = itemType;
                    ob.sy = prop.property("Type").value;
                    ob.d = prop.property("Shape Direction").value;
                    ob.pt = bm_keyframeHelper.exportKeyframes(prop.property('Points'), frameRate);
                    ob.pt.ix = prop.property('Points').propertyIndex;
                    ob.p = bm_keyframeHelper.exportKeyframes(prop.property('Position'), frameRate);
                    ob.p.ix = prop.property('Position').propertyIndex;
                    ob.r = bm_keyframeHelper.exportKeyframes(prop.property('Rotation'), frameRate);
                    ob.r.ix = prop.property('Rotation').propertyIndex;
                    if(ob.sy === 1) {
                        ob.ir = bm_keyframeHelper.exportKeyframes(prop.property('Inner Radius'), frameRate);
                        ob.ir.ix = prop.property('Inner Radius').propertyIndex;
                        ob.is = bm_keyframeHelper.exportKeyframes(prop.property('Inner Roundness'), frameRate);
                        ob.is.ix = prop.property('Inner Roundness').propertyIndex;
                    }
                    ob.or = bm_keyframeHelper.exportKeyframes(prop.property('Outer Radius'), frameRate);
                    ob.or.ix = prop.property('Outer Radius').propertyIndex;
                    ob.os = bm_keyframeHelper.exportKeyframes(prop.property('Outer Roundness'), frameRate);
                    ob.os.ix = prop.property('Outer Roundness').propertyIndex;
                    ob.ix = prop.propertyIndex;
                } else if (itemType === shapeItemTypes.ellipse) {
                    ob = {};
                    ob.d = prop.property("Shape Direction").value;
                    ob.ty = itemType;
                    ob.s = bm_keyframeHelper.exportKeyframes(prop.property('Size'), frameRate);
                    ob.p = bm_keyframeHelper.exportKeyframes(prop.property('Position'), frameRate);
                    array.push(ob);
                } else if (itemType === shapeItemTypes.fill) {
                    ob = {};
                    ob.ty = itemType;
                    ob.fillEnabled = prop.enabled;
                    ob.c = bm_keyframeHelper.exportKeyframes(prop.property('Color'), frameRate);
                    ob.o = bm_keyframeHelper.exportKeyframes(prop.property('Opacity'), frameRate);
                } else if (itemType === shapeItemTypes.gfill) {
                    ob = {};
                    ob.ty = itemType;
                    var gradientData = bm_ProjectHelper.getGradientData(navigationShapeTree);
                    ob.c = gradientData.c;
                    ob.y = gradientData.o;
                    ob.s = bm_keyframeHelper.exportKeyframes(prop.property('Start Point'), frameRate);
                    ob.e = bm_keyframeHelper.exportKeyframes(prop.property('End Point'), frameRate);
                    ob.o = bm_keyframeHelper.exportKeyframes(prop.property('Opacity'), frameRate);
                    ob.t = prop.property('Type').value;
                    if(ob.t === 2){
                        ob.h = bm_keyframeHelper.exportKeyframes(prop.property('Highlight Length'), frameRate);
                        ob.a = bm_keyframeHelper.exportKeyframes(prop.property('Highlight Angle'), frameRate);
                    }

                } else if (itemType === shapeItemTypes.gStroke) {
                    ob = {};
                    ob.ty = itemType;
                    var gradientData = bm_ProjectHelper.getGradientData(navigationShapeTree);
                    ob.c = gradientData.c;
                    ob.y = gradientData.o;
                    ob.s = bm_keyframeHelper.exportKeyframes(prop.property('Start Point'), frameRate);
                    ob.e = bm_keyframeHelper.exportKeyframes(prop.property('End Point'), frameRate);
                    ob.o = bm_keyframeHelper.exportKeyframes(prop.property('Opacity'), frameRate);
                    ob.w = bm_keyframeHelper.exportKeyframes(prop.property('Stroke Width'), frameRate);
                    ob.lc = prop.property('Line Cap').value;
                    ob.lj = prop.property('Line Join').value;
                    ob.t = prop.property('Type').value;
                    if(ob.t === 2){
                        ob.h = bm_keyframeHelper.exportKeyframes(prop.property('Highlight Length'), frameRate);
                        ob.a = bm_keyframeHelper.exportKeyframes(prop.property('Highlight Angle'), frameRate);
                    }

                } else if (itemType === shapeItemTypes.stroke) {
                    ob = {};
                    ob.ty = itemType;
                    ob.fillEnabled = prop.enabled;
                    ob.c = bm_keyframeHelper.exportKeyframes(prop.property('Color'), frameRate);
                    ob.o = bm_keyframeHelper.exportKeyframes(prop.property('Opacity'), frameRate);
                    ob.w = bm_keyframeHelper.exportKeyframes(prop.property('Stroke Width'), frameRate);
                    ob.lc = prop.property('Line Cap').value;
                    ob.lj = prop.property('Line Join').value;
                    if (ob.lj === 1) {
                        ob.ml = prop.property('Miter Limit').value;
                    }
                    var j, jLen = prop.property('Dashes').numProperties;
                    var dashesData = [];
                    var changed = false;
                    for (j = 0; j < jLen; j += 1) {
                        if (prop.property('Dashes').property(j + 1).canSetExpression) {
                            changed = true;
                            var dashData = {};
                            var name = '';
                            if (prop.property('Dashes').property(j + 1).name.indexOf('Dash') !== -1) {
                                name = 'd';
                            } else if (prop.property('Dashes').property(j + 1).name.indexOf('Gap') !== -1) {
                                name = 'g';
                            } else if (prop.property('Dashes').property(j + 1).name === 'Offset') {
                                name = 'o';
                            }
                            dashData.n = name;
                            dashData.nm = prop.property('Dashes').property(j + 1).name.toLowerCase().split(' ').join('');
                            dashData.v = bm_keyframeHelper.exportKeyframes(prop.property('Dashes').property(j + 1), frameRate);
                            dashesData.push(dashData);
                        }
                    }
                    if (changed) {
                        ob.d = dashesData;
                    }
                } else if (itemType === shapeItemTypes.merge) {
                    ob = {};
                    ob.ty = itemType;
                    ob.mm = prop.property('ADBE Vector Merge Type').value;
                } else if (itemType === shapeItemTypes.trim) {
                    ob = {};
                    ob.ty = itemType;
                    ob.s = bm_keyframeHelper.exportKeyframes(prop.property('Start'), frameRate);
                    ob.s.ix = prop.property('Start').propertyIndex;
                    ob.e = bm_keyframeHelper.exportKeyframes(prop.property('End'), frameRate);
                    ob.e.ix = prop.property('End').propertyIndex;
                    ob.o = bm_keyframeHelper.exportKeyframes(prop.property('Offset'), frameRate);
                    ob.o.ix = prop.property('Offset').propertyIndex;
                    ob.m = prop.property('Trim Multiple Shapes').value;
                    ob.ix = prop.propertyIndex;
                } else if (itemType === shapeItemTypes.group) {
                    ob = {
                        ty : itemType,
                        it: [],
                        nm: prop.name
                    };
                    navigationShapeTree.push(prop.name);
                    iterateProperties(prop.property('Contents'), ob.it, frameRate, isText);
                    if (!isText) {
                        var trOb = {};
                        var transformProperty = prop.property('Transform');
                        trOb.ty = 'tr';
                        trOb.p = bm_keyframeHelper.exportKeyframes(transformProperty.property('Position'), frameRate);
                        trOb.p.ix = transformProperty.property('Position').propertyIndex;
                        trOb.a = bm_keyframeHelper.exportKeyframes(transformProperty.property('Anchor Point'), frameRate);
                        trOb.a.ix = transformProperty.property('Anchor Point').propertyIndex;
                        trOb.s = bm_keyframeHelper.exportKeyframes(transformProperty.property('Scale'), frameRate);
                        trOb.s.ix = transformProperty.property('Scale').propertyIndex;
                        trOb.r = bm_keyframeHelper.exportKeyframes(transformProperty.property('Rotation'), frameRate);
                        trOb.r.ix = transformProperty.property('Rotation').propertyIndex;
                        trOb.o = bm_keyframeHelper.exportKeyframes(transformProperty.property('Opacity'), frameRate);
                        trOb.o.ix = transformProperty.property('Opacity').propertyIndex;
                        if(transformProperty.property('Skew').canSetExpression) {
                            trOb.sk = bm_keyframeHelper.exportKeyframes(transformProperty.property('Skew'), frameRate);
                            trOb.sk.ix = transformProperty.property('Skew').propertyIndex;
                            trOb.sa = bm_keyframeHelper.exportKeyframes(transformProperty.property('Skew Axis'), frameRate);
                            trOb.sa.ix = transformProperty.property('Skew Axis').propertyIndex;
                        }
                        trOb.nm = transformProperty.name;
                        ob.it.push(trOb);
                    }
                    navigationShapeTree.pop();
                } else if (itemType === shapeItemTypes.roundedCorners) {
                    ob = {
                        ty : itemType,
                        nm: prop.name
                    };
                    ob.r = bm_keyframeHelper.exportKeyframes(prop.property('Radius'), frameRate);
                    //bm_eventDispatcher.log(prop.property('ADBE Vector RoundCorner Radius'));
                }
                if (ob) {
                    ob.nm = prop.name;
                    var layerAttributes = bm_generalUtils.findAttributes(prop.name);
                    if(layerAttributes.ln){
                        ob.ln = layerAttributes.ln;
                    }
                    if(layerAttributes.cl){
                        ob.cl = layerAttributes.cl;
                    }
                    array.push(ob);
                }
            }
            
        }
    }
    
    
    
    
    

    function getPoint(p1, p2, p3, p4, t) {
        var a = p1[0], b = p2[0], c = p3[0], d = p4[0];
        var x = a * Math.pow(1 - t, 3) + b * 3 * Math.pow(1 - t, 2) * t + c * 3 * (1 - t) * Math.pow(t, 2) + d * Math.pow(t, 3);
        a = p1[1];
        b = p2[1];
        c = p3[1];
        d = p4[1];
        var y = a * Math.pow(1 - t, 3) + b * 3 * Math.pow(1 - t, 2) * t + c * 3 * (1 - t) * Math.pow(t, 2) + d * Math.pow(t, 3);
        return [x, y];
    }

    function getTPos(p1, p2, p3, p4, arr) {
        var i;
        for (i = 0; i < 2; i += 1) {
            var c1 = p1[i], c2 = p2[i], c3 = p3[i], c4 = p4[i];
            var a = 3 * (-c1 + 3 * c2 - 3 * c3 + c4);
            var b = 6 * (c1 - 2 * c2 + c3);
            var c = 3 * (c2 - c1);
            var toSquareTerm = Math.pow(b, 2) - 4 * a * c;
            if (toSquareTerm >= 0) {
                var t1 = (-b + Math.sqrt(toSquareTerm)) / (2 * a);
                var t2 = (-b - Math.sqrt(toSquareTerm)) / (2 * a);
                if (t1 >= 0 && t1 <= 1) {
                    arr.push(getPoint(p1, p2, p3, p4, t1));
                }
                if (t2 >= 0 && t2 <= 1) {
                    arr.push(getPoint(p1, p2, p3, p4, t2));
                }
            }
        }
    }

    function getBoundingBox(p1, p2, p3, p4, bounds) {
        var pts = [p1,p4];
        getTPos(p1, p2, p3, p4, pts);

        var minX = bounds.l, minY = bounds.t, maxX = bounds.r, maxY = bounds.b, pt;
        var i, len = pts.length;
        for (i = 1; i < len; i += 1) {
            pt = pts[i];
            if (minX > pt[0]) {
                minX = pt[0];
            }
            if (maxX < pt[0]) {
                maxX = pt[0];
            }
            if (minY > pt[1]) {
                minY = pt[1];
            }
            if (maxY < pt[1]) {
                maxY = pt[1];
            }
        }
        bounds.l = minX;
        bounds.t = minY;
        bounds.r = maxX;
        bounds.b = maxY;
    }

    function setBounds(shapeData, bounds, matrices, strokes, data) {
        var arr = [];
        var i, len = matrices.length;
        for (i = 0; i < len; i += 1) {
            matrices[i].getKeys(arr);
        }
        var shapeProp;
        if (shapeData.ty === 'sh') {
            shapeProp = PropertyFactory.getShapeProp(data, shapeData, 4, [], []);
        } else if (shapeData.ty === 'rc') {
            shapeProp = PropertyFactory.getShapeProp(data, shapeData, 5, [], []);
        } else if (shapeData.ty === 'el') {
            shapeProp = PropertyFactory.getShapeProp(data, shapeData, 6, [], []);
        } else if (shapeData.ty === 'sr') {
            shapeProp = PropertyFactory.getShapeProp(data, shapeData, 7, [], []);
        }
        shapeProp.getKeys(arr);
        var j, jLen = arr.length, matr = new Matrix();
        for (j = 0; j < jLen; j += 1) {
            data.globalData.frameId += 1;
            data.comp.renderedFrame = arr[j];
            matr.reset();
            for (i = 0; i < len; i += 1) {
                matrices[i].getValue();
                matr.transform(matrices[i].v.props[0], matrices[i].v.props[1], matrices[i].v.props[2], matrices[i].v.props[3], matrices[i].v.props[4], matrices[i].v.props[5]);
            }
            if (shapeProp.k) {
                shapeProp.getValue();
            }
            var points = shapeProp.v;
            var k, kLen = points.v.length;
            for (k = 0; k < kLen - 1; k += 1) {
                getBoundingBox(matr.applyToPointArray(points.v[k][0], points.v[k][1]), matr.applyToPointArray(points.o[k][0]+points.v[k][0], points.o[k][1]+points.v[k][1]), matr.applyToPointArray(points.i[k + 1][0]+points.v[k + 1][0], points.i[k + 1][1]+points.v[k + 1][1]), matr.applyToPointArray(points.v[k + 1][0], points.v[k + 1][1]), bounds);
            }
            getBoundingBox(matr.applyToPointArray(points.v[k][0], points.v[k][1]), matr.applyToPointArray(points.o[k][0]+points.v[k][0], points.o[k][1]+points.v[k][1]), matr.applyToPointArray(points.i[0][0]+points.v[0][0], points.i[0][1]+points.v[0][1]), matr.applyToPointArray(points.v[0][0], points.v[0][1]), bounds);
        }
        len = strokes.length;
        arr = [];
        for (i = 0; i < len; i += 1) {
            strokes[i].getKeys(arr);
        }
        jLen = arr.length;
        var maxStroke = 0;
        for (j = 0; j < jLen; j += 1) {
            data.globalData.frameId += 1;
            data.comp.renderedFrame = arr[j];
            for (i = 0; i < len; i += 1) {
                if (strokes[i].k) {
                    strokes[i].getValue();
                }
                maxStroke = strokes[i].v > maxStroke ? strokes[i].v :  maxStroke;
            }
        }
        if (maxStroke) {
            bounds.t -= maxStroke / 2;
            bounds.l -= maxStroke / 2;
            bounds.b += maxStroke / 2;
            bounds.r += maxStroke / 2;
        }
        bounds.t = Math.floor(bounds.t);
        bounds.l = Math.floor(bounds.l);
        bounds.b = Math.ceil(bounds.b);
        bounds.r = Math.ceil(bounds.r);
    }
    

    function completeShapes(arr, bounds, matrices, strokes, data) {
        var i, len = arr.length;
        var j, jLen;
        var matr = [];
        var strk = [];
        matr = matr.concat(matrices);
        strk = strk.concat(strokes);
        for (i = len - 1; i >= 0; i -= 1) {
            if (arr[i].ty === 'sh' || arr[i].ty === 'el' || arr[i].ty === 'rc' || arr[i].ty === 'sr') {
                setBounds(arr[i], bounds, matr, strk, data);
            } else if (arr[i].ty === 'gr') {
                completeShapes(arr[i].it, bounds, matr, strk, data);
            } else if (arr[i].ty === 'tr') {
                matr.push(PropertyFactory.getProp(data, arr[i], 2, 0, []));
            } else if (arr[i].ty === 'st') {
                strk.push(PropertyFactory.getProp(data, arr[i].w, 0, 0, []));
            }
        }
    }
    
    function getShapeBounds(data) {
        data.bounds = {
            l: 999999,
            t: 999999,
            b: -999999,
            r: -999999
        };
        completeShapes(data.shapes, data.bounds, [], [], {data: {st: 0}, globalData: {frameId: 0}, comp: {renderedFrame: -1}});
    }
    
    
    function exportShape(layerInfo, layerOb, frameRate, isText) {
        var containingComp = layerInfo.containingComp;
        navigationShapeTree.length = 0;
        navigationShapeTree.push(containingComp.name);
        navigationShapeTree.push(layerInfo.name);
        //bm_eventDispatcher.log('iddd: ' + layerInfo.id);
        var shapes = [], contents = layerInfo.property('Contents');
        layerOb.shapes = shapes;
        iterateProperties(contents, shapes, frameRate, isText);
        /*if (!isText) {
            getShapeBounds(layerOb);
        }*/
        getShapeBounds(layerOb);
    }
    
    ob.exportShape = exportShape;
    
    return ob;
}());