# Source: core/media-controller.js

    /**
     * @module montage/ui/controller/media-controller
     * @requires montage/core/core
     * @requires montage/ui/component
     * @requires montage/core/logger
     */
    var Target = require("./target").Target,
        logger = require("./logger").logger("mediacontroller");

    /**
     * @class MediaController
     * @classdesc Controls an audio/video media player.
     * @extends Target
     */
    var MediaController = exports.MediaController = Target.specialize(/** @lends MediaController# */ {

        /*-----------------------------------------------------------------------------
         MARK:   Constants
         -----------------------------------------------------------------------------*/

        /**
         * @type {number}
         * @default {number} 0
         */
        STOPPED: { value: 0, writable: false },

        /**
         * @type {number}
         * @default {number} 1
         */
        PLAYING: { value: 1, writable: false },

        /**
         * @type {number}
         * @default {number} 2
         * */
        PAUSED: { value: 2, writable: false },

        /**
         * @type {number}
         * @default {number} 3
         */
        EMPTY: { value: 3, writable: false },

        _TIMEUPDATE_FREQUENCY: { value: 0.25 },  // Don't refresh too often.

        /*-----------------------------------------------------------------------------
         MARK:   Properties
         -----------------------------------------------------------------------------*/

        _mediaElement: {
            value: null
        },

        mediaElement: {
            get: function () {
                return this._mediaElement;
            },
            set: function (mediaElement) {
                if (this._mediaElement !== mediaElement) {
                    if (this._mediaElement) {
                        this._removeControlEventHandlers();
                    }

                    this._mediaElement = mediaElement;
                    this._installControlEventHandlers();
                }
            }
        },

        /*-----------------------------------------------------------------------------
         MARK:   Status & Attributes
         -----------------------------------------------------------------------------*/

        _status: {
            value: 3
        },

        /**
         * @type {Function}
         * @default {number} 3
         */
        status: {
            get: function () {
                return this._status;
            },
            set: function (status) {
                if (status !== this._status) {
                    if (logger.isDebug) {
                        logger.debug("MediaController:status: " + status);
                    }
                    this._status = status;
                }
            }
        },

        _position: {
            value: null
        },

        /**
         * @type {Function}
         * @default null
         */
        position: {
            set: function (time, shouldNotUpdate) {
                this._position = time;
                if (!shouldNotUpdate) {
                    this._pauseTime = null;
                    this.currentTime = time;
                }
            },
            get: function () {
                return this._position;
            }
        },

        _duration: {
            value: null
        },

        /**
         * @type {Function}
         * @default null
         */
        duration: {
            set: function (time) {
                if (isNaN(time)) {
                    if (logger.isDebug) {
                        logger.debug("MediaController:setDuration: duration is not valid");
                    }
                    return;
                }
                if (logger.isDebug) {
                    logger.debug("MediaController:setDuration: duration=" + time);
                }
                this._duration = time;
            },
            get: function () {
                return this._duration;
            }
        },

        /*-----------------------------------------------------------------------------
         MARK:   Media Player Commands
         -----------------------------------------------------------------------------*/

        /**
         * @type {number}
         * @default {boolean} true
         */
        autoplay: {
            value: false
        },

        /**
         * @function
         */
        play: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:play()");
                }

                // setting currentTime will throw if video not loaded yet(?)
                if (this._mediaElement.currentTime !== 0) {
                    this._mediaElement.currentTime = 0;
                }
                this._mediaElement.play();
                this._pauseTime = null;
            }
        },

        _pauseTime: {
            value: null
        },

        /**
         * @function
         */
        pause: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:pause()");
                }
                // temporary workaround for Chrome issue: https://code.google.com/p/chromium/issues/detail?id=242839
                this._pauseTime = this._mediaElement.currentTime;
                this._mediaElement.pause();
            }
        },

        /**
         * @function
         */
        unpause: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:unpause()");
                }

                if (this._pauseTime !== null) {
                    this._mediaElement.currentTime = this._pauseTime;
                }

                this._mediaElement.play();
            }
        },

        /**
         * @function
         * @returns {boolean} !playing (true if it is now playing)
         */
        playPause: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:playPause()");
                }

                var playing = (this.status === this.PLAYING),
                    paused = (this.status === this.PAUSED);

                this.playbackRate = this._mediaElement.defaultPlaybackRate;

                if (playing) {
                    this.pause();
                } else if (paused) {
                    this.unpause();
                } else {
                    this.play();
                }

                return !playing;    // true if it is now playing
            }
        },

        _playbackRate: {
            value: 1
        },

        /**
         * @type {Function}
         * @default {number} 1
         */
        playbackRate: {
            get: function () {
                return this._playbackRate;
            },
            set: function (playbackRate) {
                if (this._playbackRate !== playbackRate) {
                    this._playbackRate = playbackRate;
                    this._mediaElement.playbackRate = this._playbackRate;
                }
            }
        },

        defaultPlaybackRate: {
            set: function (defaultPlaybackRate) {
                return (this._mediaElement.defaultPlaybackRate = defaultPlaybackRate);
            },
            get: function () {
                return this._mediaElement.defaultPlaybackRate;
            }
        },

        /**
         * @type {Function}
         * @default {number} 0
         */
        currentTime: {
            get: function () {
                return this._mediaElement.currentTime;
            },
            set: function (currentTime) {
                if (this.status === this.EMPTY) {
                    return;
                }

                try {
                    if (isNaN(this._mediaElement.duration)) {
                        logger.error("MediaController:set currentTime: duration is not valid");
                        return;
                    }

                    var oldTime = this._mediaElement.currentTime;

                    if (oldTime !== currentTime) {
                        if (this._position !== currentTime && this.status !== this.STOPPED) {
                            this._position = currentTime;
                        }

                        this._mediaElement.currentTime = currentTime;
                    }

                } catch (err) {
                    logger.error("MediaController:Exception in set currentTime" + this._mediaElement.currentTime);
                }
            }
        },

        /**
         * @function
         */
        rewind: {
            value: function () {
                if (this.status === this.PLAYING) {
                    if (logger.isDebug) {
                        logger.debug("MediaController:rewind()");
                    }

                    this.playbackRate = -4.0;
                }
            }
        },

        /**
         * @function
         */
        fastForward: {
            value: function () {
                if (this.status === this.PLAYING) {
                    if (logger.isDebug) {
                        logger.debug("MediaController:fastForward()");
                    }

                    this.playbackRate = 4.0;
                }
            }
        },

        /**
         * @function
         */
        stop: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:stop()");
                }

                // Pause the playback
                this._mediaElement.pause();
                this._pauseTime = null;
                // Reset the status
                this.status = this.STOPPED;
                this.position = 0;
            }
        },

        /*-----------------------------------------------------------------------------
         MARK:   Volume Commands
         -----------------------------------------------------------------------------*/

        /**
         * @type {Function}
         * @returns {number} this.mediaElement.volume * 100
         */
        volume: {
            get: function () {
                return this._mediaElement.volume * 100;
            },

            set: function (vol) {
                var volume = vol;
                if (typeof volume === 'undefined') {
                    volume = 50;
                }
                else if (volume > 100) {
                    volume = 100;
                }
                else if (volume < 0) {
                    volume = 0;
                }
                this._mediaElement.volume = volume / 100.0;
            }
        },

        /**
         * @function
         */
        volumeIncrease: {
            value: function () {
                this.volume += 10;
            }
        },

        /**
         * @function
         */
        volumeDecrease: {
            value: function () {
                this.volume -= 10;
            }
        },

        /**
         * @function
         */
        toggleMute: {
            value: function () {
                this.mute = !this.mute;
            }
        },

        /**
         * @type {Function}
         */
        mute: {
            get: function () {
                return this._mediaElement.muted;
            },
            set: function (muted) {
                if (muted !== this._mediaElement.muted) {
                    this._mediaElement.muted = muted;
                }
            }
        },

        /*-----------------------------------------------------------------------------
         MARK:   Event Handlers
         -----------------------------------------------------------------------------*/

        /**
         * @function
         * @returns itself
         */
        handleLoadedmetadata: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:handleLoadedmetadata: PLAYING=" + (this.status === this.PLAYING) + " duration=" + this.mediaController.duration);
                }
                if (isNaN(this._mediaElement.duration)) {
                    if (logger.isDebug) {
                        logger.debug("MediaController:handleLoadedmetadata: duration is not valid");
                    }
                    return;
                }
                this.duration = this._mediaElement.duration;
                if (this.autoplay) {
                    if (logger.isDebug) {
                        logger.debug("MediaController:handleLoadedmetadata: autoplay");
                    }
                    this.play();
                } else {
                    this.status = this.STOPPED;
                }
            }
        },

        _lastCurrentTime: {
            value: 0
        },

        /**
         * @function
         */
        handleTimeupdate: {
            value: function () {
                if (this.status !== this.STOPPED) { // A last 'timeupdate' is sent after stop() which is unwanted because it restores the last position.
                    var currentTime = this._mediaElement.currentTime;
                    //if (Math.abs(this._lastCurrentTime - currentTime) >= this._TIMEUPDATE_FREQUENCY) {
                    //    this._lastCurrentTime = currentTime;
                    Object.getPropertyDescriptor(this, "position").set.call(this, currentTime, true);
                    //}
                }
            }
        },

        /**
         * @function
         */
        handlePlay: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:handlePlay");
                }
                this.status = this.PLAYING;
            }
        },

        /**
         * @function
         */
        handlePlaying: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:handlePlaying: PLAYING");
                }
                this.status = this.PLAYING;
            }
        },

        /**
         * @function
         */
        handlePause: {
            value: function () {
                if (this.status !== this.STOPPED) {
                    if (logger.isDebug) {
                        logger.debug("MediaController:handlePause: PAUSED");
                    }
                    this.status = this.PAUSED;
                }
                else {
                    if (logger.isDebug) {
                        logger.debug("MediaController:handlePause: STOPPED");
                    }
                }
            }
        },

        /**
         * @function
         */
        handleEnded: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:handleEnded");
                }
                // If the media controller is not in the paused=true state
                // then it won't fire a play event when you start playing again
                this._mediaElement.pause();
                this.status = this.STOPPED;
            }
        },

        /**
         * @function
         */
        handleAbort: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:handleAbort: STOPPED");
                }
                this.status = this.STOPPED;
            }
        },

        /**
         * @function
         * @param {Event} event TODO
         */
        handleError: {
            value: function (event) {
                if (logger.isDebug) {
                    logger.debug("MediaController:handleError: STOPPED");
                }
                var error = event.target.error;

                this.status = this.STOPPED;

                if (error) {
                    switch (error.code) {
                        case error.MEDIA_ERR_ABORTED:
                            console.error("You aborted the video playback.");
                            break;
                        case error.MEDIA_ERR_NETWORK:
                            console.error("A network error caused the video download to fail part-way.");
                            break;
                        case error.MEDIA_ERR_DECODE:
                            console.error("The video playback was aborted due to a corruption problem or because the video used features your browser did not support.");
                            break;
                        case error.MEDIA_ERR_SRC_NOT_SUPPORTED:
                            console.error("The selected video could not be loaded, either because the server or network failed, the format is not supported, or no video has been selected.");
                            break;
                        default:
                            console.error("An unknown error occurred.");
                            break;
                    }
                }
            }
        },

        /**
         * @function
         */
        handleEmptied: {
            value: function () {
                if (logger.isDebug) {
                    logger.debug("MediaController:handleEmptied: STOPPED");
                }

                this.status = this.STOPPED;
            }
        },

        _installControlEventHandlers: {
            value: function () {
                this._mediaElement.addEventListener('loadedmetadata', this);
                this._mediaElement.addEventListener('timeupdate', this);
                this._mediaElement.addEventListener('play', this);
                this._mediaElement.addEventListener('playing', this);
                this._mediaElement.addEventListener('pause', this);
                this._mediaElement.addEventListener('abort', this);
                this._mediaElement.addEventListener('error', this);
                this._mediaElement.addEventListener('emptied', this);
                this._mediaElement.addEventListener('ended', this);
            }
        },

        _removeControlEventHandlers: {
            value: function () {
                this._mediaElement.removeEventListener('loadedmetadata', this);
                this._mediaElement.removeEventListener('timeupdate', this);
                this._mediaElement.removeEventListener('play', this);
                this._mediaElement.removeEventListener('playing', this);
                this._mediaElement.removeEventListener('pause', this);
                this._mediaElement.removeEventListener('abort', this);
                this._mediaElement.removeEventListener('error', this);
                this._mediaElement.removeEventListener('emptied', this);
                this._mediaElement.removeEventListener('ended', this);
            }
        }

    }, {
        blueprintModuleId:require("./core")._blueprintModuleIdDescriptor,

        blueprint:require("./core")._blueprintDescriptor,

        objectDescriptorModuleId:require("./core")._objectDescriptorModuleIdDescriptor,

        objectDescriptor:require("./core")._objectDescriptorDescriptor

    });