/**
 * @private
 */
Ext.define('Ext.fx.animation.Abstract', {

    extend: 'Ext.Evented',

    isAnimation: true,

    requires: [
        'Ext.fx.State'
    ],

    config: {
        name: '',

        element: null,

        /**
         * @cfg
         * Before configuration.
         */
        before: null,

        from: {},

        to: {},

        after: null,

        states: {},

        duration:  300,

        /**
         * @cfg
         * Easing type.
         */
        easing: 'linear',

        iteration: 1,

        direction: 'normal',

        delay: 0,

        onBeforeStart: null,

        callback: null,

        onEnd: null,

        onBeforeEnd: null,

        scope: null,

        reverse: null,

        preserveEndState: false,

        replacePrevious: true
    },

    STATE_FROM: '0%',

    STATE_TO: '100%',

    DIRECTION_UP: 'up',

    DIRECTION_DOWN: 'down',

    DIRECTION_LEFT: 'left',

    DIRECTION_RIGHT: 'right',

    stateNameRegex: /^(?:[\d\.]+)%$/,

    constructor: function() {
        this.states = {};

        this.callParent(arguments);

        return this;
    },

    applyElement: function(element) {
        return Ext.get(element);
    },

    applyBefore: function(before, current) {
        if (before) {
            return Ext.factory(before, Ext.fx.State, current);
        }
    },

    applyAfter: function(after, current) {
        if (after) {
            return Ext.factory(after, Ext.fx.State, current);
        }
    },

    setFrom: function(from) {
        return this.setState(this.STATE_FROM, from);
    },

    setTo: function(to) {
        return this.setState(this.STATE_TO, to);
    },

    getFrom: function() {
        return this.getState(this.STATE_FROM);
    },

    getTo: function() {
        return this.getState(this.STATE_TO);
    },

    setStates: function(states) {
        var validNameRegex = this.stateNameRegex,
            name;

        for (name in states) {
            if (validNameRegex.test(name)) {
                this.setState(name, states[name]);
            }
        }

        return this;
    },

    getStates: function() {
        return this.states;
    },

    updateCallback: function(callback) {
        if (callback) {
            this.setOnEnd(callback);
        }
    },

    end: function() {
        // alias for stop so that the following api is the same between ext/touch:
        // element.getActiveAnimation().end()
        this.stop();
    },

    stop: function() {
        this.fireEvent('stop', this);
    },

    destroy: function() {
        this.stop();
        this.callParent();
    },

    setState: function(name, state) {
        var states = this.getStates(),
            stateInstance;

        stateInstance = Ext.factory(state, Ext.fx.State, states[name]);

        if (stateInstance) {
            states[name] = stateInstance;
        }
        //<debug>
        else if (name === this.STATE_TO) {
            Ext.Logger.error("Setting and invalid '100%' / 'to' state of: " + state);
        }
        //</debug>

        return this;
    },

    getState: function(name) {
        return this.getStates()[name];
    },

    getData: function() {
        var me = this,
            states = me.getStates(),
            statesData = {},
            before = me.getBefore(),
            after = me.getAfter(),
            from = states[me.STATE_FROM],
            to = states[me.STATE_TO],
            fromData = from.getData(),
            toData = to.getData(),
            data, name, state;

        for (name in states) {
            if (states.hasOwnProperty(name)) {
                state = states[name];
                data = state.getData();
                statesData[name] = data;
            }
        }

        return {
            before: before ? before.getData() : {},
            after: after ? after.getData() : {},
            states: statesData,
            from: fromData,
            to: toData,
            duration: me.getDuration(),
            iteration: me.getIteration(),
            direction: me.getDirection(),
            easing: me.getEasing(),
            delay: me.getDelay(),
            onEnd: me.getOnEnd(),
            onBeforeEnd: me.getOnBeforeEnd(),
            onBeforeStart: me.getOnBeforeStart(),
            scope: me.getScope(),
            preserveEndState: me.getPreserveEndState(),
            replacePrevious: me.getReplacePrevious()
        };
    }
});
