/**
 * 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.
         * Defaults to false when you are on Android, as it doesnt support controls.
         * @accessor
         */
        enableControls: Ext.os.is.Android ? false : 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. Default is 1.
         * @accessor
         */
        volume: 1,

        /**
         * @cfg {Boolean} muted
         * Whether or not the media is muted. This will also set the volume to zero. Default is false.
         * @accessor
         */
        muted: false
    },

    initialize: function() {
        var me = this;
        me.callParent();

        me.on({
            scope: me,

            activate  : me.onActivate,
            deactivate: me.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;

        if (!Ext.isObject(event)) {
            var oldEvent = event;
            event = {};
            event[oldEvent] = fn;
        }

        Ext.Object.each(event, function(e, fn) {
            if (typeof fn !== 'function') {
                fn = me[fn];
            }

            if (typeof fn == 'function') {
                fn = bind(fn, me);

                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.getAutoResume() && 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.media.dom.controls = enableControls ? 'controls' : false;
    },

    /**
     * Updates the loop setting of the media element.
     */
    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();
            setTimeout(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;
     */
    getCurrentTime: function() {
        return this.media.dom.currentTime;
    },

    /*
     * Set the current time of the media.
     * @param {Number} time The time in seconds
     */
    setCurrentTime: function(time) {
        this.media.dom.currentTime = time;

        return time;
    },

    /**
     * Returns the duration of the media in seconds;
     */
    getDuration: function() {
        return this.media.dom.duration;
    },

    destroy: function() {
        var me = this;
        Ext.Object.each(event, function(e, fn) {
            if (typeof fn !== 'function') {
                fn = me[fn];
            }

            if (typeof fn == 'function') {
                fn = bind(fn, me);

                dom.removeEventListener(e, fn);
            }
        });
    }
});