/**
 * @private
 * @class Ext.draw.sprite.AnimationParser
 *
 * Computes an intermidiate value between two values of the same type for use in animations.
 * Can have pre- and post- processor functions if the values need to be processed
 * before an intermidiate value can be computed (parseInitial), or the computed value
 * needs to be processed before it can be used as a valid attribute value (serve).
 */
Ext.define('Ext.draw.sprite.AnimationParser', function () {

    function compute(from, to, delta) {
        return from + (to - from) * delta;
    }

    return {
        singleton: true,
        attributeRe: /^url\(#([a-zA-Z\-]+)\)$/,
        requires: ['Ext.draw.Color'],

        color: {
            parseInitial: function (color1, color2) {
                if (Ext.isString(color1)) {
                    color1 = Ext.draw.Color.create(color1);
                }
                if (Ext.isString(color2)) {
                    color2 = Ext.draw.Color.create(color2);

                }
                if ((color1 instanceof Ext.draw.Color) && (color2 instanceof Ext.draw.Color)) {
                    return [
                        [color1.r, color1.g, color1.b, color1.a],
                        [color2.r, color2.g, color2.b, color2.a]
                    ];
                } else {
                    return [color1 || color2, color2 || color1];
                }
            },
            compute: function (from, to, delta) {
                if (!Ext.isArray(from) || !Ext.isArray(to)) {
                    return to || from;
                } else {
                    return [compute(from[0], to[0], delta), compute(from[1], to[1], delta), compute(from[2], to[2], delta), compute(from[3], to[3], delta)];

                }
            },
            serve: function (array) {
                var color = Ext.draw.Color.fly(array[0], array[1], array[2], array[3]);
                return color.toString();
            }
        },

        number: {
            parse: function (n) {
                return n === null ? null : +n;
            },

            compute: function (from, to, delta) {
                if (!Ext.isNumber(from) || !Ext.isNumber(to)) {
                    return to || from;
                } else {
                    return compute(from, to, delta);
                }
            }
        },

        angle: {
            parseInitial: function (from, to) {
                if (to - from > Math.PI) {
                    to -= Math.PI * 2;
                } else if (to - from < -Math.PI) {
                    to += Math.PI * 2;
                }
                return [from, to];
            },

            compute: function (from, to, delta) {
                if (!Ext.isNumber(from) || !Ext.isNumber(to)) {
                    return to || from;
                } else {
                    return compute(from, to, delta);
                }
            }
        },

        path: {
            parseInitial: function (from, to) {
                var fromStripes = from.toStripes(),
                    toStripes = to.toStripes(),
                    i, j,
                    fromLength = fromStripes.length, toLength = toStripes.length,
                    fromStripe, toStripe,
                    length,
                    lastStripe = toStripes[toLength - 1],
                    endPoint = [lastStripe[lastStripe.length - 2], lastStripe[lastStripe.length - 1]];

                for (i = fromLength; i < toLength; i++) {
                    fromStripes.push(fromStripes[fromLength - 1].slice(0));
                }
                for (i = toLength; i < fromLength; i++) {
                    toStripes.push(endPoint.slice(0));
                }
                length = fromStripes.length;

                toStripes.path = to;
                toStripes.temp = new Ext.draw.Path();

                for (i = 0; i < length; i++) {
                    fromStripe = fromStripes[i];
                    toStripe = toStripes[i];
                    fromLength = fromStripe.length;
                    toLength = toStripe.length;
                    toStripes.temp.commands.push('M');
                    for (j = toLength; j < fromLength; j += 6) {
                        toStripe.push(endPoint[0], endPoint[1], endPoint[0], endPoint[1], endPoint[0], endPoint[1]);
                    }

                    lastStripe = toStripes[toStripes.length - 1];
                    endPoint = [lastStripe[lastStripe.length - 2], lastStripe[lastStripe.length - 1]];
                    for (j = fromLength; j < toLength; j += 6) {
                        fromStripe.push(endPoint[0], endPoint[1], endPoint[0], endPoint[1], endPoint[0], endPoint[1]);
                    }
                    for (i = 0; i < toStripe.length; i++) {
                        toStripe[i] -= fromStripe[i];
                    }
                    for (i = 2; i < toStripe.length; i += 6) {
                        toStripes.temp.commands.push('C');
                    }
                }

                return [fromStripes, toStripes];
            },

            compute: function (fromStripes, toStripes, delta) {
                if (delta >= 1) {
                    return toStripes.path;
                }
                var i = 0, ln = fromStripes.length,
                    j = 0, ln2, from, to,
                    temp = toStripes.temp.params, pos = 0;

                for (; i < ln; i++) {
                    from = fromStripes[i];
                    to = toStripes[i];
                    ln2 = from.length;
                    for (j = 0; j < ln2; j++) {
                        temp[pos++] = to[j] * delta + from[j];
                    }
                }
                return toStripes.temp;
            }
        },

        data: {
            compute: function (from, to, delta, target) {
                var lf = from.length - 1,
                    lt = to.length - 1,
                    len = Math.max(lf, lt),
                    f, t, i;

                if (!target || target === from) {
                    target = [];
                }
                target.length = len + 1;

                for (i = 0; i <= len; i++) {
                    f = from[Math.min(i, lf)];
                    t = to[Math.min(i, lt)];
                    if (Ext.isNumber(f)) {
                        if (!Ext.isNumber(t)) {
                            // This may not give the desired visual result during
                            // animation (after all, we don't know what the target
                            // value should be, if it wasn't given to us), but it's
                            // better than spitting out a bunch of NaNs in the target
                            // array, when transitioning from a non-empty to an empty
                            // array.
                            t = 0;
                        }
                        target[i] = (t - f) * delta + f;
                    } else {
                        target[i] = t;
                    }
                }

                return target;
            }
        },

        text: {
            compute: function (from, to, delta) {
                return from.substr(0, Math.round(from.length * (1 - delta))) + to.substr(Math.round(to.length * (1 - delta)));
            }
        },

        limited: 'number',
        limited01: 'number'
    };
});