(function () {
 
var logger = ST.logger.forClass('MagicCanvasRecorder');
 
/**
 * @class ST.event.MagicCanvasRecorder
 * @extend ST.event.Recorder
 * This class is not created by user code. It is created by the Sencha Test Event Recorder
 * in Sencha Studio via the injected {@link ST#startRecording} method call.
 */
ST.event.MagicCanvasRecorder = ST.define({
    extend: ST.event.Recorder,
 
    constructor: function (config) {
        var me = this,
            Event = ST.event.Event;
 
        me.eventMaps = [
            Event.clickEvents,
            {
                // We intentionally do not record keypress so as to simplify the output. 
                // When a keydown event is played back, a keypress will be simulated 
                // immediately after. 
                keydown: 1,
                keyup: 1
            },
            {
                mousedown: 1,
                mouseup: 1,
                pointerdown: 1,
                pointerup: 1,
                mousewheel: 1,
                wheel: 1
            }
            // TODO any more events??? 
        ];
 
        ST.apply(me, config);
        me.previousEvent = {};
        me.clear();
    },
 
    wrapEvent: function(e) {
        var me = this;
 
        return new Promise(function (resolve, reject) {
            return ST.defaultContext.STLoaded().then(function () {
                logger.trace('Wrapping', e.type);
                if (e.type === 'wheel' || ST.event.Event.mouseEvents[e.type] || ST.event.Event.clickEvents[e.type] || ST.event.Event.keyEvents[e.type]) {
                    return ST.defaultContext.wrapEvent(e)
                        .then(function (remoteEvent) {
                            var event = remoteEvent.value,
                                playable;
 
                            // TODO: make the recorder UI use the targets array instead of forcing a target here 
                            if (event.targets.length) {
                                event.target = event.targets[0].target;
                            }
 
                            // playback the event in the target browser because we are intercepting events in the canvas 
                            // this lets us check and make sure we recorded the right things 
                            // (locator, action, etc.) before adding it to the recorded stack 
                            playable = ST.defaultContext.initEvent(event);
                            if (ST.event.Event.keyEvents[e.type]) {
                                if (!playable.key) playable.key = e.key;
                            }
 
                            logger.tick('MagicCanvasRecorder.wrapEvent injecting playable ' + playable.type)
                            ST.defaultContext.inject(playable, function () {
                                me.previousEvent = event;
                                resolve(event);
                            }, function (err) {
                                // If we fall in here, something happened to the element during injection. We want to go 
                                // ahead and send the event on to Studio for recording. 
                                resolve(event);
                            });
                        });
                } else if (e.type === 'focus' || e.type === 'blur') {
                    // focus and blur on window have problems that need investigation... 
                } else if (ST.event.Event.mouseEvents[e.type]) {
                    // mouse events need to be handled by the webdriverrecorder context maybe? 
                } else {
                    // TODO What did we miss? Do we need to inject it? 
                    logger.error('Unsupported BrowserEvent occurred.', e);
                    reject('Unsupported BrowserEvent occurred.' + e.type);
                }
            }).catch(function (err) {
                return ST.defaultContext._loadST().then(function () {
                    // We got an event that couldn't properly resolve in the target. 
                    // This is usually as a result of navigation, in particular with keydown events 
                    // that trigger a navigation (Enter, space bar on a button/link, etc.) and their 
                    // closing keyup events. 
                    // Set the target according to the previous event for coalescing as a best guess. 
                    if (ST.event.Event.keyEvents[e.type]) {
                        var event = new ST.event.Event(e, []);
                        if (me.previousEvent) {
                            event.target = me.previousEvent.target;
                            event.targets = me.previousEvent.targets;
 
                            resolve(event.serialize());
                        }
                    }
                })
            });
        });
    },
 
    flush: function() {
        var me = this,
            recording = me.recording,
            eventOrder = me.eventOrder,
            length = recording.length,
            events = recording.slice(me.flushIndex, length),
            fixedEvents = [],
            len = eventOrder.length, i, x, id;
 
        // on the MagicCanvas, events can be processed out of the order in which they were originally detected 
        // to bypass this mess, we'll simply fix up the order of the recordings based on the order of their ids 
        // since we know the order in which they were originally detected, we can simply put them back into the right 
        // sequence when the flush is ready 
        for (i=0; i<len; i++) {
            id = eventOrder[i];
 
            for (x=0; x<events.length; x++) {
                if (id === events[x].recorderId) {
                    fixedEvents.push(events[x]);
                    break;
                }
            }
        }
        me.fireEvent('add', me, fixedEvents);
        me.flushIndex = length;
        clearTimeout(me.flushTimeout);
        me.flushTimeout = null;
    }
});
 
}());