/**
 * @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.util.Color.create(color1);
                }
 
                if (Ext.isString(color2)) {
                    color2 = Ext.util.Color.create(color2);
 
                }
 
                if ((color1 && color1.isColor) && (color2 && color2.isColor)) {
                    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.util.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 (= fromLength; i < toLength; i++) {
                    fromStripes.push(fromStripes[fromLength - 1].slice(0));
                }
 
                for (= toLength; i < fromLength; i++) {
                    toStripes.push(endPoint.slice(0));
                }
 
                length = fromStripes.length;
 
                toStripes.path = to;
                toStripes.temp = new Ext.draw.Path();
 
                for (= 0; i < length; i++) {
                    fromStripe = fromStripes[i];
                    toStripe = toStripes[i];
                    fromLength = fromStripe.length;
                    toLength = toStripe.length;
                    toStripes.temp.commands.push('M');
 
                    for (= 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 (= fromLength; j < toLength; j += 6) {
                        fromStripe.push(endPoint[0], endPoint[1], endPoint[0], endPoint[1],
                                        endPoint[0], endPoint[1]);
                    }
 
                    for (= 0; i < toStripe.length; i++) {
                        toStripe[i] -= fromStripe[i];
                    }
 
                    for (= 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;
                }
 
                // eslint-disable-next-line vars-on-top
                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 (= 0; j < ln2; j++) {
                        temp[pos++] = to[j] * delta + from[j];
                    }
                }
 
                return toStripes.temp;
            }
        },
 
        data: {
            compute: function(from, to, delta, target) {
                var iMaxFrom = from.length - 1,
                    iMaxTo = to.length - 1,
                    iMax = Math.max(iMaxFrom, iMaxTo),
                    i, start, end;
 
                if (!target || target === from) {
                    target = [];
                }
 
                target.length = iMax + 1;
 
                for (= 0; i <= iMax; i++) {
                    start = from[Math.min(i, iMaxFrom)];
                    end = to[Math.min(i, iMaxTo)];
 
                    if (Ext.isNumber(start)) {
                        if (!Ext.isNumber(end)) {
                            // 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.
                            end = 0;
                        }
 
                        target[i] = start + (end - start) * delta;
                    }
                    else {
                        target[i] = end;
                    }
                }
 
                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'
    };
});