/**
 * Provides a base class for audio/visual controls. Should not be used directly.
 *
 * Please see the {@link Ext.Audio} and {@link Ext.Video} classes for more information.
 * @private
 */
Ext.define('Ext.Media', {
    extend: 'Ext.Component',
    xtype: 'media',
 
    /**
     * @event play
     * Fires whenever the media is played.
     * @param {Ext.Media} this
     */
 
    /**
     * @event pause
     * Fires whenever the media is paused.
     * @param {Ext.Media} this
     * @param {Number} time The time at which the media was paused at in seconds.
     */
 
    /**
     * @event ended
     * Fires whenever the media playback has ended.
     * @param {Ext.Media} this
     * @param {Number} time The time at which the media ended at in seconds.
     */
 
    /**
     * @event stop
     * Fires whenever the media is stopped.
     * The `pause` event will also fire after the `stop` event if the media is currently playing.
     * The `timeupdate` event will also fire after the `stop` event regardless of playing status.
     * @param {Ext.Media} this
     */
 
    /**
     * @event volumechange
     * Fires whenever the volume is changed.
     * @param {Ext.Media} this
     * @param {Number} volume The volume level from 0 to 1.
     */
 
    /**
     * @event mutedchange
     * Fires whenever the muted status is changed.
     * The volumechange event will also fire after the `mutedchange` event fires.
     * @param {Ext.Media} this
     * @param {Boolean} muted The muted status.
     */
 
    /**
     * @event timeupdate
     * Fires when the media is playing every 15 to 250ms.
     * @param {Ext.Media} this
     * @param {Number} time The current time in seconds.
     */
 
    config: {
        /**
         * @cfg {String} url
         * Location of the media to play.
         * @accessor
         */
        url: '',
 
        /**
         * @cfg {Boolean} enableControls
         * Set this to `false` to turn off the native media controls.
         * @accessor
         * @deprecated 6.5 Please use {@link #controls} instead.
         */
        enableControls: true,
 
        /**
         * @cfg {Boolean} controls
         * Set this to `false` to turn off the native media controls.
         * @accessor
         */
        controls: true,
 
        /**
         * @cfg {Boolean} autoResume
         * Will automatically start playing the media when the container is activated.
         * @accessor
         */
        autoResume: false,
 
        /**
         * @cfg {Boolean} autoPause
         * Will automatically pause the media when the container is deactivated.
         * @accessor
         */
        autoPause: true,
 
        /**
         * @cfg {Boolean} preload
         * Will begin preloading the media immediately.
         * @accessor
         */
        preload: true,
 
        /**
         * @cfg {Boolean} loop
         * Will loop the media forever.
         * @accessor
         */
        loop: false,
 
        /**
         * @cfg {Ext.Element} media
         * A reference to the underlying audio/video element.
         * @accessor
         */
        media: null,
 
        /**
         * @cfg {Number} volume
         * The volume of the media from 0.0 to 1.0.
         * @accessor
         */
        volume: 1,
 
        /**
         * @cfg {Boolean} muted
         * Whether or not the media is muted. This will also set the volume to zero.
         * @accessor
         */
        muted: false
    },
 
    constructor: function(config) {
        this.mediaEvents = {};
        this.callParent([config]);
    },
 
    initialize: function() {
        var me = this;
 
        me.callParent();
 
        me.on({
            scope: me,
            show: 'onActivate',
            hide: 'onDeactivate'
        });
 
        me.addMediaListener({
            canplay: 'onCanPlay',
            play: 'onPlay',
            pause: 'onPause',
            ended: 'onEnd',
            volumechange: 'onVolumeChange',
            timeupdate: 'onTimeUpdate'
        });
    },
 
    addMediaListener: function(event, fn) {
        var me = this,
            dom = me.media.dom,
            bind = Ext.Function.bind;
 
        Ext.Object.each(event, function(e, fn) {
            fn = bind(me[fn], me);
            me.mediaEvents[e] = fn;
            dom.addEventListener(e, fn);
        });
    },
 
    onPlay: function() {
        this.fireEvent('play', this);
    },
 
    onCanPlay: function() {
        this.fireEvent('canplay', this);
    },
 
    onPause: function() {
        this.fireEvent('pause', this, this.getCurrentTime());
    },
 
    onEnd: function() {
        this.fireEvent('ended', this, this.getCurrentTime());
    },
 
    onVolumeChange: function() {
        this.fireEvent('volumechange', this, this.media.dom.volume);
    },
 
    onTimeUpdate: function() {
        this.fireEvent('timeupdate', this, this.getCurrentTime());
    },
 
    /**
     * Returns if the media is currently playing.
     * @return {Boolean} playing `true` if the media is playing.
     */
    isPlaying: function() {
        return !Boolean(this.media.dom.paused);
    },
 
    /**
     * @private
     */
    onActivate: function() {
        var me = this;
 
        if (me.getAutoResume() && !me.isPlaying()) {
            me.play();
        }
    },
 
    /**
     * @private
     */
    onDeactivate: function() {
        var me = this;
 
        if (me.getAutoPause() && me.isPlaying()) {
            me.pause();
        }
    },
 
    /**
     * Sets the URL of the media element. If the media element already exists, it is update the src attribute of the
     * element. If it is currently playing, it will start the new video.
     */
    updateUrl: function(newUrl) {
        var dom = this.media.dom;
 
        //when changing the src, we must call load:
        //http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/ControllingMediaWithJavaScript/ControllingMediaWithJavaScript.html
 
        dom.src = newUrl;
 
        if ('load' in dom) {
            dom.load();
        }
 
        if (this.isPlaying()) {
            this.play();
        }
    },
 
    /**
     * Updates the controls of the video element.
     */
    updateEnableControls: function(enableControls) {
        this.setControls(enableControls);
    },
 
    updateControls: function (value) {
        this.media.set({
            controls: value ? 'controls' : undefined
        });
    },
 
    /**
     * Updates the loop setting of the media element.
     * @param {Boolean} loop
     */
    updateLoop: function(loop) {
        this.media.dom.loop = loop ? 'loop' : false;
    },
 
    /**
     * Starts or resumes media playback.
     */
    play: function() {
        var dom = this.media.dom;
 
        if ('play' in dom) {
            dom.play();
            Ext.defer(function() {
                dom.play();
            }, 10);
        }
    },
 
    /**
     * Pauses media playback.
     */
    pause: function() {
        var dom = this.media.dom;
 
        if ('pause' in dom) {
            dom.pause();
        }
    },
 
    /**
     * Toggles the media playback state.
     */
    toggle: function() {
        if (this.isPlaying()) {
            this.pause();
        } else {
            this.play();
        }
    },
 
    /**
     * Stops media playback and returns to the beginning.
     */
    stop: function() {
        var me = this;
 
        me.setCurrentTime(0);
        me.fireEvent('stop', me);
        me.pause();
    },
 
    /**
     * @private
     */
    updateVolume: function(volume) {
        this.media.dom.volume = volume;
    },
 
    /**
     * @private
     */
    updateMuted: function(muted) {
        this.fireEvent('mutedchange', this, muted);
 
        this.media.dom.muted = muted;
    },
 
    /**
     * Returns the current time of the media, in seconds.
     * @return {Number}
     */
    getCurrentTime: function() {
        return this.media.dom.currentTime;
    },
 
    /**
     * Set the current time of the media.
     * @param {Number} time The time, in seconds.
     * @return {Number}
     */
    setCurrentTime: function(time) {
        this.media.dom.currentTime = time;
 
        return time;
    },
 
    /**
     * Returns the duration of the media, in seconds.
     * @return {Number}
     */
    getDuration: function() {
        return this.media.dom.duration;
    },
 
    doDestroy: function() {
        var me = this,
            dom  = me.media.dom,
            mediaEvents = me.mediaEvents;
 
        Ext.Object.each(mediaEvents, function(event, fn) {
            dom.removeEventListener(event, fn);
        });
 
        me.callParent();
    },
 
    deprecated: {
        '6.5': {
            configs: {
                enableControls: {
                    message: 'Please use "controls" instead.'
                }
            }
        }
    }
});