Source: components/oldplayer.js

define('application/components/oldplayer', [
    'rofl/widgets/component',
    'antie/runtimecontext',
    'application/managers/api',
    'application/managers/session',
    'rofl/lib/utils',
    'rofl/events/keyevent',
    'application/managers/channel',
    'rofl/lib/promise',
    'application/managers/progress',
    'rofl/devices/mediaplayer/mediaplayer',
    'application/views/player',
    'application/widgets/warmwelcome/overlay',
    'product-layer/player/widgets/seeker',
    'application/widgets/player/liveseeker',
    'application/decorators/player/manipulation',
    'rofl/analytics/web/google',
    'rofl/media/source',
    'application/widgets/player/warningbox',
    'rofl/lib/l10n',
    'rofl/logging/graylog',
    'application/managers/player',
    'application/managers/bookmark',
    'application/helpers/parentalHelper',
    'application/events/unlockedevent',
    'application/models/epg/item',
    'application/events/bingewatchevent',
    'application/events/nextepisodeevent'
], function (
    Component,
    RuntimeContext,
    ApiManager,
    SessionManager,
    Utils,
    KeyEvent,
    ChannelManager,
    Promise,
    ProgressManager,
    MediaPlayer,
    PlayerView,
    WarmWelcome,
    Seeker,
    LiveSeeker,
    PlayerManipulation,
    GoogleAnalytics,
    MediaSource,
    WarningBox,
    L10N,
    Graylog,
    PlayerManager,
    BookmarkManager,
    ParentalHelper,
    UnlockedEvent,
    EPGItem,
    BingeWatchEvent,
    NextEpisodeEvent
) {
    'use strict';

    var application = RuntimeContext.getCurrentApplication(),
        api = ApiManager.getKPNAPI(),
        ApiErrorCodes = ApiManager.getApiErrorCodes(),
        channelManager = ChannelManager.getInstance(),
        device = RuntimeContext.getDevice(),
        configuration = application.getConfiguration(),
        mediaPlayer = device.getMediaPlayer(),
        sessionManager = SessionManager.getInstance(),
        LIVE_PROGRESS_TIMEOUT = configuration.player.liveProgressTimeout,
        KEY_HOLD_TIMEOUT = configuration.player.keyHoldTimeout,
        GA = GoogleAnalytics.getInstance(),
        l10n = L10N.getInstance(),
        graylog = Graylog.getInstance(),
        TYPES = {
            LIVE: 'LIVE',
            VOD: 'VOD',
            VOD_MOVIE: 'VOD_MOVIE',
            RESTART: 'RESTART',
            RECORDING: 'RECORDING',
            PROMO: 'PROMO'
        },
        EVENT = {
            startVideo: 'StartVideo',
            stopVideo: 'StopVideo',
            startOver: 'StartOver',
            trickplay: 'Trickplay'
        },
        DIRECTION = {
            forward: 'forward',
            backward: 'backward'
        },
        GALabels = {
            live: 'LiveTV',
            restart: 'Restart',
            vod: 'CatchUp',
            recording: 'Recording',
            forward: 'Forward',
            backward: 'Backward',
            vod_movie: 'VOD',
            series: 'SERIES'
        },
        PlayerComponent,
        zapping = false,

        // to include all the promises to channel up/down
        promiseStack = [];

    PlayerComponent = Component.extend({

        /**
         * Initialises the player component.
         *
         * @param {string} [id] - Id of the player. Optional.
         */
        init: function init (id) {
            init.base.call(this, id || 'player');

            application.showLoader(true, true);
            this._deviceBrand = device.getBrand();
            this.decorate([PlayerManipulation]);
            this._build();
            this._setBindings();
            this._warningBox = new WarningBox();
            this.appendChildWidget(this._warningBox);
            this._showErrorOnPlayerFocus = null;
            this._previousChannelResolved = true;
            this._playbackStatus = {};
            this._playbackStatus.active = false;
        },

        /**
         * Sets the bindings.
         *
         * @private
         */
        _setBindings: function () {

            this._onSeekSpeedChangeBound = Utils.bind(this._onSeekSpeedChanged, this);
            this._onSeekCurrentTimeChangeBound = Utils.bind(this._onSeekCurrentTimeChanged, this);
            this._onSeekBound = Utils.bind(this._onSeek, this);
            this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
            this._onKeyUpBound = Utils.bind(this._onKeyUp, this);
            this._onControlKeyDownBound = Utils.bind(this._onControlKeyDown, this);
            this._onSelectBound = Utils.bind(this._onSelect, this);
            this._onFocusBound = Utils.bind(this._onFocus, this);
            this._multitaskHandler = Utils.bind(this._onVisibilityChanged, this);
            this._onMouseSeekEventBound = Utils.bind(this._onMouseSeekEvent, this);
            this._onParentalControlChangedEventBound = Utils.bind(this._onParentalControlChangedEvent, this);
        },

        /**
         * Builds the seeker.
         *
         * @private
         */
        _buildSeeker: function () {
            this._seeker = Seeker();

            this._seeker.attach(
                this._onSeekSpeedChangeBound,
                this._onSeekCurrentTimeChangeBound,
                this._onSeekBound,
                configuration.player.seekSteps,
                configuration.player.turnTrickplayImmediately
            );
        },

        /**
         * Builds the live seeker.
         *
         * @private
         */
        _buildLiveSeeker: function () {
            var program = this._program,
                playbackTime = application.getDate().getTime(),
                startTime = program.getStartTime(),
                endTime = program.getEndTime();

            this._seeker = LiveSeeker();

            this._seeker.attach({
                onSpeedChanged: this._onSeekSpeedChangeBound,
                onSeek: this._onSeekBound,
                onCurrentTimeUpdated: this._onSeekCurrentTimeChangeBound,
                steps: configuration.player.seekSteps
            });

            if (this._pauseTime) {
                playbackTime = this._pauseTime;
            }

            this._streamStart = playbackTime;

            this._seeker.setProgramTime(startTime * 1000, endTime * 1000, playbackTime);
        },

        /**
         * Builds the player component.
         *
         * @private
         */
        _build: function () {
            var playerView = this._view = new PlayerView(),
                warmWelcome = this._warmWelcome = new WarmWelcome();

            this.appendChildWidget(warmWelcome);
            this.appendChildWidget(playerView);
        },

        /**
         * BeforeRender event.
         */
        onBeforeRender: function () {
            this._loadingTime = application.getDate();

            this._showErrorOnPlayerFocus = null;
        },

        /**
         * BeforeShow event.
         *
         * @param {Object} e - The event parameters.
         */
        onBeforeShow: function (e) {
            var args = e.args || {},
                videoType = args.type,
                subType = args.subType || null;

            this._bingewatch = this._nextEpisodes = args.bingewatch;
            this._analyticsType = args.analyticsType || null;
            this._lastPlayerArguments = args;
            this._triggerParentalOnFocus = false;
            this._subType = subType;
            this._callingPage = args.callingPage;
            this._backButtonActive = false;
            this._seekCurrentTime = null;
            this._setEventListeners();
            this._watchingVideoTimeStart = application.getDate();

            if (!(args.channelId
                && (this._lastChannelId === args.channelId)
                && videoType === TYPES.LIVE
                && videoType === this._type)) {
                this._resetPlayer(false, false, true);
            }

            switch (videoType) {
                case TYPES.LIVE:
                    this._onPlayLive(args);
                    break;
                case TYPES.RESTART:
                    this._onPlayRestart(args);
                    break;
                case TYPES.RECORDING:
                    this._onPlayRecording(args);
                    break;
                case TYPES.VOD:
                case TYPES.VOD_MOVIE:
                    this._onPlayVOD(args);
                    break;
                case TYPES.PROMO:
                    this._playPromo(args);
                    break;
            }

            this._view.showUI(true);
            this._pauseTime = application.getDate().getTime();
        },

        _onPlayLive: function (args) {
            var videoType = args.type;

            if (!(args.channelId
                && (this._lastChannelId === args.channelId)
                && videoType === this._type)
                || this._parentalEscaped) {

                this._playChannel(args.channelId);
            } else {

                this._view.getMiniEPG().setChannel(args.channelId);
                application.hideLoader();
            }
        },

        _onPlayRestart: function (args) {
            this._onRestart(args.channelId, args.startTime);
            this._restartButtonPressed = true;
        },

        _onPlayRecording: function (args) {
            if (this._mediaChangeInProgress) {
                this._afterMediaChangeStuff = Utils.bind(function () {
                    this._playRecording(args);
                }, this);
            } else {
                this._mediaChangeInProgress = true;
                this._playRecording(args);
            }
        },

        _onPlayVOD: function (args) {
            if (this._mediaChangeInProgress) {
                    this._afterMediaChangeStuff = Utils.bind(function () {
                        this._mediaChangeInProgress = true;
                        this._playVOD(args);
                    }, this);
                } else {
                this._mediaChangeInProgress = true;
                this._playVOD(args);
            }
        },

        /**
         * Route to the specified page after player has been initialized.
         *
         * @param {string} page - The page to route.
         * @param {Number|undefined} channelId - The last channel ID.
         */
        routeToPage: function (page, channelId) {
            var main = application.getComponent('main');

            channelId = channelId || null;

            application.route(page, {
                lastChannelId: channelId,
                callback: Utils.bind(function () {
                    this.setStyleTo('display', 'none');
                    main.setStyleTo('display', 'block');
                }, this)
            });
        },

        /**
         * Activates the warm welcome.
         *
         * @private
         */
        _activateWarmWelcome: function () {
            var warmWelcome = this._warmWelcome;

            application.hideLoader();
            sessionManager.increaseWarmWelcomeDisplayCount();
            warmWelcome.activate(true);
            warmWelcome.focus();

            this._view.hideUI({
                skipAnim: true
            });
        },

        /**
         * Returns true if the player should show the warm welcome.
         *
         * @returns {boolean} - True if should show warm welcome.
         * @private
         */
        _shouldActivateWarmWelcome: function () {
            return sessionManager.shouldShowWarmWelcome();
        },

        /**
         * Sets the event listeners.
         *
         * @private
         */
        _setEventListeners: function () {

            this._setTizenMultitaskHandler();
            this.addEventListener('focus', this._onFocusBound);
            this.addEventListener('keydown', this._onKeyDownBound);
            this.addEventListener('keyup', this._onKeyUpBound);
            this.addEventListener('select', this._onSelectBound);
            this.addEventListener('mouseseek', this._onMouseSeekEventBound);
            application.addEventListener('$parentalcontrol', this._onParentalControlChangedEventBound);
            this._view.getControls().addEventListener('keydown', this._onControlKeyDownBound);
            mediaPlayer.addEventCallback(this, this._onPlayerEvent);
        },

        /**
         * Removes the event listeners.
         *
         * @private
         */
        _removeEventListeners: function () {

            this._removeTizenMultitaskHandler();
            this.removeEventListener('focus', this._onFocusBound);
            this.removeEventListener('keydown', this._onKeyDownBound);
            this.removeEventListener('keyup', this._onKeyUpBound);
            this.removeEventListener('select', this._onSelectBound);
            this.removeEventListener('mouseseek', this._onMouseSeekEventBound);
            application.removeEventListener('$parentalcontrol', this._onParentalControlChangedEventBound);
            this._view.getControls().removeEventListener('keydown', this._onControlKeyDownBound);
            mediaPlayer.removeEventCallback(this, this._onPlayerEvent);
        },

        /**
         * Select event.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onSelect: function (e) {
            var target = e.target,
                dataItem,
                programDetails;

            if (this._warmWelcome.isActive()) {
                this._onSelectWarmWelcome();
                e.preventDefault();
                e.stopPropagation();
                return;
            }

            if (this._view.isUIVisible()) {

                switch (target.id) {
                    case 'menuBtn':
                        this._onShownMenu();
                        break;
                    case 'info':
                        programDetails = this._view.getProgramDetails();

                        if (programDetails.hasClass('expand')) {
                            this._onInfo(false, null);
                        } else {
                            this._setDetailInfo();
                        }
                        break;
                    case 'rewind':
                        this._onRewind();
                        break;
                    case 'playpause':
                        this._onPlayPause();
                        break;
                    case 'fastforward':
                        this._onFastForward();
                        break;
                    case 'restartBtn':
                        if (!this._restartButtonPressed && !this._mediaChangeInProgress) {

                            if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
                                mediaPlayer.playFrom(0);
                            } else {
                                this._resetPlayer();
                                this._onRestart();
                                this._restartButtonPressed = true;
                            }

                            GA.onEvent('Action', EVENT.startOver);
                        }
                        break;
                    case 'recordBtn':
                        this._onRecord();
                        break;
                    case 'now-next':
                        this._onMiniEPG();
                        break;
                    case 'next-episode':
                        if (!this._mediaChangeInProgress) {
                            this._onNextEpisode(this._nextEpisodes.splice(0, 1)[0]);
                        }
                        this._view.closeExpandedContents();
                        break;
                    case 'back-button':
                        this._view.closeExpandedContents();
                        this._onBack();
                        break;
                    case 'liveBtn':
                        this._onLive();
                        break;
                }

                if (target.hasClass('carouselItem')) {
                    dataItem = target.getChildWidgetByIndex(0).getDataItem().item;

                    if (dataItem.isLocked() && !sessionManager.getUserPin()) {
                        application.route('parentalpin', {
                            successCallback: Utils.bind(function () {
                                this._onMiniEPGProgram(target);
                            }, this),
                            errorCallback: Utils.bind(function (result) {

                                if (Utils.isUndefined(result)) {
                                    this._view.getControls().focus();
                                    this._view.getMiniEPG().focus();
                                }
                            }, this),
                            escapeCallback: Utils.bind(function () {
                                this._view.getControls().focus();
                                this._view.getMiniEPG().focus();
                            }, this),
                            closeOnError: false
                        });
                    } else {

                        this._onMiniEPGProgram(target);
                    }
                }
            } else {

                this._view.showUI(true);
            }
        },

        /**
         * Gets executed when a program is selected from the mini epg.
         *
         * @param {Object} target - The target program.
         * @private
         */
        _onMiniEPGProgram: function (target) {
            this._view.getControls().focus();
            this._view.closeExpandedContents();
            this._playChannel(target.getDataItem());

            GA.onEvent(
                'Action',
                'MiniEPGProgramSelected',
                {
                    eventLabel: 'LiveTV'
                });
        },

        /**
         * Function solves selecting restart button.
         *
         * @param {number} [inputChannelId] - Number of channel.
         * @param {number} [inputStartTime] - Time number of start of the program.
         * @private
         */
        _onRestart: function (inputChannelId, inputStartTime) {
            var channelId = inputChannelId || null,
                startTime = inputStartTime || null;

            this._restartStarted = true;

            if (channelId && startTime) {
                this._getCurrentBroadcast(channelId)
                    .then(Utils.bind(function (epgItem) {

                        // Set the new program data.
                        this._view.setProgram({program: epgItem, type: this._type});
                        this._program = epgItem;
                    }, this))
                    .then(Utils.bind(this._prepareRestartStream, this))
                    ['catch'](Utils.bind(this._onPlayerError, this));
            } else {

                if (this._type === TYPES.LIVE) {
                    this._type = TYPES.RESTART;
                }

                if (this._type !== TYPES.RECORDING) {
                    this._view.setProgram({
                        program: this._program,
                        type: this._type,
                        hasNextEpisode: !!(this._nextEpisodes && this._nextEpisodes.length)
                    });
                }

                this._prepareRestart();
            }
        },

        /**
         * Prepares restart of playback.
         *
         * @private
         */
        _prepareRestart: function () {
            switch (this._type) {
                case TYPES.RESTART:
                    this._prepareRestartStream();
                    break;
                case TYPES.VOD_MOVIE:
                    this._prepareRestartVOD();
                    break;
                case TYPES.RECORDING:
                    this._mediaChangeInProgress = false;
                    this._recordingContentRestarted = true;
                    this._onPlayRecording({
                        data: this._program
                    });
                    break;
                case TYPES.VOD:
                    this._mediaChangeInProgress = false;
                    this._vodContentRestarted = true;
                    this._onPlayVOD({
                        data: this._program,
                        type: TYPES.VOD
                    });
                    break;
            }
        },

        /**
         * Prepares the restart stream.
         *
         * @private
         */
        _prepareRestartStream: function () {
            var program = this._program,
                startTime = application.getDate();

            this._prepareMediaSource({
                type: TYPES.RESTART,
                assetId: program.getChannel().getAssetId(),
                contentId: program.getChannelId(),
                startTime: program.getStartTime() * 1000
            }).then(Utils.bind(function (mediaSource) {

                if (this._delayedStreamTime) {
                    startTime = this._delayedStreamTime;
                }

                this._playMediaSource(mediaSource);
                this._startRestartProgressTimeOut(startTime);
                this._setCurrentTimeToSeeker();
            }, this))
            ['catch'](Utils.bind(this._onPlayerError, this));
        },

        /**
         * Prepares the restart VOD.
         *
         * @private
         */
        _prepareRestartVOD: function () {
            var program = this._program;

            this._vodContentRestarted = true;
            this._prepareMediaSource({
                type: TYPES.VOD_MOVIE,
                assetId: program.getAssetId(),
                contentId: program.getId()
            }).then(Utils.bind(function (mediaSource) {
                this._playMediaSource(mediaSource);
                this._view.setVODProgress({
                    percentage: 0,
                    currentTime: 0,
                    duration: this._program.getDuration()
                });
                this._restartStarted = false;
                this._restartButtonPressed = false;
            }, this))
            ['catch'](Utils.bind(this._onPlayerError, this));
        },

        /**
         * Handle switching the channel with numeric input.
         *
         * @param {Object} e - The event parameters.
         * @private
         */
        _handleSwitchChannel: function (e) {
            var view = this._view;

            if (!this._canSwitchChannel()) {
                return;
            }

            if (this._keys.length >= 3) {
                this._keys = '';
            }

            this._keys += e.keyChar;

            if (view.isUIVisible()) {
                this._view.hideUI({
                    duration: 500
                });

            }

            this._setChannelSwitchNumber();
        },

        /**
         * Sets the channel switching number.
         *
         * @private
         */
        _setChannelSwitchNumber: function () {
            var view = this._view,
                channelSwitcher = view.getChannelSwitcher(),
                timeoutValue = this._deviceBrand === 'samsung' ? 3000 : 1000;

            clearTimeout(this._inputTimeout);
            channelSwitcher.setLabel(this._keys);

            this._inputTimeout = setTimeout(Utils.bind(function () {

                this._switchChannel(channelManager.getChannelByNumber(this._keys));
                this._keys = '';
            }, this), timeoutValue);
        },

        /**
         * BeforeHide event.
         */
        onBeforeHide: function () {
            var value = ((application.getDate() - this._watchingVideoTimeStart) / 60000).toFixed(2),
                channelId;

            if (channelManager.getChannels().length > 0) {

                // TODO: Temporary get channel by id (NPO 1). Otherwise last watched: channelManager.getLastWatchedChannel()
                channelId = channelManager.getChannelById(18).getId() || channelManager.getChannelByNumber(1).getId();

                GA.onEvent('player', 'time', {
                    eventLabel: channelId,
                    eventValue: value
                });

                if (this._warmWelcome.isActive()) {
                    this._warmWelcome.activate(false);
                }

                this._removeEventListeners();
            }
        },

        /**
         * PlayerEvent.
         *
         * @param {Object} e - The player event data.
         * @private
         */
        _onPlayerEvent: function (e) {
            var playerComponent = application.getComponent('player'),
                main = application.getComponent('main'),
                landingPage = main.getChildWidget('landingPage'),
                detail = application.getComponent('detail'),
                view = this._view,
                channelId,
                startTime,
                timeOfLoadingChannel;

            switch (e.type) {

                case MediaPlayer.EVENT.PLAYING:

                    BookmarkManager.onPlayStart(this._contentId, this._type);

                    if (zapping) {
                        zapping = false;

                        timeOfLoadingChannel = application.getDate() - this._switchChannelStart;
                        channelId = this._program.getChannelId();

                        GA.onEvent('player', 'zap', {
                            eventLabel: channelId,
                            eventValue: timeOfLoadingChannel
                        });
                    }

                    if (this._delayedStreamTime && this._type === TYPES.RESTART) {
                        this._delayedSeek();
                    }

                    if (this._delayedBack) {
                        this._delayedBack();
                        this._delayedBack = null;
                    }

                    if (playerComponent.hasClass('focus') && this._canShowUI()) {
                        if (!this._seeker.isActive()) {
                            view.showUI(true);
                        }
                    }

                    application.hideLoader(2000);
                    break;

                case MediaPlayer.EVENT.PAUSED:

                    BookmarkManager.onPause();

                    if (this._progress !== null && (this._progress.behind >= 0 || isNaN(this._progress.behind))) {
                        this._pauseTime = application.getDate().getTime();
                    }

                    view.onPause();
                    view.showUI(false);
                    this._playbackStatus.active = false;
                    break;

                case MediaPlayer.EVENT.STATUS:

                    // Set playack status to active after receiving the first STATUS event after PLAYING.
                    this._playbackStatus.active = true;

                    if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
                        this._setVODProgress(e);
                    }

                    break;

                case MediaPlayer.EVENT.COMPLETE:

                    if (this._bingewatchActive) {
                        return;
                    }

                    this._resetPlayer();
                    channelId = this._program.getChannelId();

                    if (this._type === TYPES.RECORDING) {

                        if (this._callingPage === 'detail') {
                            this._callingPage = null;

                            this._playChannel(channelId);

                            this.setStyleTo('display', 'none');
                            main.setStyleTo('display', 'block');

                            detail.setStyleTo('display', 'block');
                            detail.focus();
                        }
                    } else if (this._type === TYPES.VOD_MOVIE) {
                        this._mediaChangeInProgress = false;
                        this._onBack();
                    } else if (this._subType === 'promo') {

                        this.setStyleTo('display', 'none');
                        this._removeEventListeners();
                        landingPage.togglePlayers(false);
                    } else {
                        startTime = this._program.getStartTime();
                        this._checkNextPlayableItem(channelId, startTime);
                    }

                    this._playbackStatus.active = false;
                    break;

                case MediaPlayer.EVENT.ERROR:
                    this._playbackStatus.active = false;
                    this._onPlayerError(e.message);
                    break;

                case MediaPlayer.EVENT.BITRATE_CHANGED:
                    this._reportBitrateChange(e);
                    break;

                // No default.
            }
        },

        /**
         * Returns next playable item in channel.
         *
         * @param {number} channelId - Id of current channel.
         * @param {number} itemStartTime - Start time of current item.
         * @private
         */
        _checkNextPlayableItem: function (channelId, itemStartTime) {
            channelManager.getNextBroadcastAtTime(channelId, itemStartTime)
                .then(Utils.bind(function (epgItem) {
                    var args = {};

                    if (!epgItem) {
                        this._playChannel(channelId);

                        this._delayedBack = function () {
                            this._onBack();
                        };
                    } else if (epgItem.isLive()) {

                        if (epgItem.isLocked()) {
                            application.route('parentalpin', {
                                successCallback: Utils.bind(function () {
                                    this._playChannel(channelId);
                                    application.broadcastEvent(new UnlockedEvent());
                                }, this),
                                errorCallback: Utils.bind(function (result) {

                                    if (Utils.isUndefined(result)) {
                                        this.routeToPage('channellist', channelId);
                                    }
                                }, this),
                                escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true),
                                callingPageKeyEvent: {
                                    keyCodes: [KeyEvent.VK_CHANNEL_UP, KeyEvent.VK_CHANNEL_DOWN],
                                    keyEventCallback: Utils.bind(this._skipCl, this, epgItem)
                                }
                            });
                        } else {
                            this._playChannel(channelId);
                        }
                    } else if (epgItem.canPlay() && epgItem.isPlayable()) {
                        api.read('detail', {
                            params: {
                                endpoint: epgItem.getDetailsAction()
                            },
                            withCredentials: true
                        }).then(Utils.bind(function (broadcast) {
                            args.data = broadcast;
                            this._playVOD(args);

                        }, this));
                    } else {
                        itemStartTime = epgItem.getStartTime();
                        this._checkNextPlayableItem(channelId, itemStartTime);
                    }

                }, this));
        },

        /**
         * Attempts to play a channel.
         *
         * @param {string} [channelId] - The channel id.
         * @param {boolean} [reset] - True if should reset channelId.
         * @private
         */
        _playChannel: function (channelId, reset) {
            var assetId;

            this._bingewatch = this._nextEpisodes = null;

            if (!channelId) {

                if (!reset) {

                    // TODO: Temporary get channel by ID (NPO 1). Otherwise last watched: channelManager.getLastWatchedChannel()
                    channelId = channelManager.getChannelById(18).getId() || channelManager.getChannelByNumber(1).getId();
                } else {
                    channelId = channelManager.getChannelByNumber(1).getId();
                }

                /*
                 * We are playing something for the first time.
                 * Stop the old playback first to lower the concurrent stream amount.
                 */
                this._program = null;
                this._type = TYPES.LIVE;
            }

            this._checkingParental = false;
            this._lastChannelId = channelId;
            assetId = channelManager.getChannelById(channelId).getAssetId();

            this._type = TYPES.LIVE;

            // Save last played channel id
            channelManager.setLastWatchedChannel(channelId);

            this._loadChannelData(channelId, assetId)
                .then(Utils.bind(this._onChannelReady, this))
                ['catch'](Utils.bind(this._onPlayerError, this));
        },

        /**
         * Plays the VOD stream.
         *
         * @param {Object} args - The arguments.
         * @private
         */
        _playVOD: function (args) {
            var data = args.data,
                channelId = data.getChannelId(),
                type = args.type,
                self = this,
                goToChannelListOnEscapeParental = channelId ? true : false,
                playContent = function () {
                    application.showLoader(true, true);
                    self._resetPlayer();

                    self._view.setProgram({
                        program: data,
                        type: self._type,
                        hasNextEpisode: !!(self._nextEpisodes && self._nextEpisodes.length)
                    });

                    self._prepareMediaSource({
                        startTime: channelId ? new Date(data.getStartTime() * 1000).getTime() : null,
                        endTime: channelId ? new Date(data.getEndTime() * 1000).getTime() : null,
                        assetId: data.getAssetId(),
                        contentId: data.getId(),
                        type: self._type,

                        // NOTE: Pay attention to this. Is it right?
                        replayAssetId: channelId ? channelManager.getChannelById(channelId).getAssetId() : null,
                        replayContentId: channelId
                    })
                        .then(Utils.bind(function (mediaSource) {
                            self._playMediaSource(mediaSource);
                        }, this))
                        ['catch'](Utils.bind(self._onPlayerError, self));
                };

            self._view.hideProgress({skipAnim: true});

            this._type = type || TYPES.VOD;
            this._program = data;

            if (!sessionManager.getUserPin() && data.isLocked()) {

                if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
                    mediaPlayer.pause();
                }

                application.route('parentalpin', {
                    successCallback: function () {
                        playContent();
                        application.broadcastEvent(new UnlockedEvent());
                    },
                    errorCallback: Utils.bind(function (result) {

                        if (Utils.isUndefined(result)) {
                            ParentalHelper.parentaleEscapeCallback.call(this, goToChannelListOnEscapeParental);
                        }
                    }, this),
                    escapeCallback: Utils.bind(ParentalHelper.parentaleEscapeCallback, this, goToChannelListOnEscapeParental)
                });
            } else {
                playContent();
            }

        },

        /**
         * Plays the Promo video.
         *
         * @param {Object} args - The arguments.
         * @private
         */
        _playPromo: function (args) {
            var data = args.data,
                promoUrl = args.promo_url,
                mediaSource;

            this._resetPlayer();

            this._type = TYPES.VOD;
            this._program = data;
            this._view.setPromo(data, 'promo');
            this._isPromo = true;

            mediaSource = new MediaSource(promoUrl);

            this._playMediaSource(mediaSource);
            application.showPlayer();
        },

        /**
         * Plays the RECORDING stream.
         *
         * @param {Object} args - The arguments.
         * @private
         */
        _playRecording: function (args) {

            var data = args.data;

            this._resetPlayer();

            this._program = data;
            this._type = TYPES.RECORDING;
            this._view.setProgram({program: data, type: 'VOD'});
            this._prepareMediaSource({
                assetId: data.getAssetId(),
                contentId: data.getRecordingId(),
                type: TYPES.RECORDING,
                startTime: new Date(data.getStartDeltaTime()).getTime()
            })
                .then(Utils.bind(function (mediaSource) {
                    this._playMediaSource(mediaSource);
                }, this))
                ['catch'](Utils.bind(this._onPlayerError, this));
        },

        /**
         * Renders the UI part of the new channel.
         *
         * @param {Object[]} args       - All the arguments required to render player UI.
         *                   args[0]    - MediaSource of the channel.
         *                   args[1]    - EPG Item of the channel.
         * @private
         */
        _onChannelUI: function (args) {
            var epgItem = args[1],
                view = this._view;

            // If we came from channel switching, hide the input.
            this._view.getChannelSwitcher().setLabel('');
            this._keys = '';
            this._program = epgItem;

            view.setProgram({program: epgItem, type: this._type});

            if (this._warmWelcome.isActive()) {
                view.disablePointerSupport();
            }

            if (this._canShowUI()) {
                view.showUI(false);
            }

            this._startLiveProgressTimeOut();
        },

        /**
         * Gets executed when the channel data finishes loading.
         *
         * @param {Array} args - The arguments.
         * @private
         */
        _onChannelReady: function (args) {
            var channelId = args[1].getChannelId();

            if (args[0] === 'locked') {

                if (!this._deferredPlayerInteraction) {
                    application.route('parentalpin', {
                        successCallback: Utils.bind(function () {
                            this._playChannel();
                            application.broadcastEvent(new UnlockedEvent());
                        }, this),
                        errorCallback: Utils.bind(function (result) {
                            if (Utils.isUndefined(result)) {
                                this.routeToPage('channellist', channelId);
                            }
                        }, this),
                        escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true)
                    });
                } else {
                    this._lastChannelId = null;
                    this._triggerParentalOnFocus = true;
                    this._mediaChangeInProgress = false;
                }
            } else {
                this._resetPlayer();
                this._onChannelUI(args);
                this._playMediaSource(args[0]);

                if (this._view.getProgramDetails().hasClass('expand')) {
                    this._setDetailInfo();
                }
            }
        },

        /**
         * Attempts to play the video.
         *
         * @param {Object} mediaSource - The mediasource.
         * @private
         */
        _playMediaSource: function (mediaSource) {
            var isLive = this._type === TYPES.LIVE,
                isRestart = this._type === TYPES.RESTART,
                isVodMovie = this._type === TYPES.VOD_MOVIE,
                isSeries = !!(isVodMovie && this._program._seriesId),
                mediaPlayerState = mediaPlayer.getState(),
                contentRestarted = this._vodContentRestarted || this._recordingContentRestarted,
                self = this,
                beginPlaybackFrom;

            this._createAndAttachSeeker();

            if (this._deviceBrand !== 'lg') {
                mediaPlayer.setMediaSource(mediaSource);
            }

            if (this._analyticsType) {
                this._GATypeLabel = this._analyticsType;
            } else {
                this._GATypeLabel = isSeries ? GALabels.series : GALabels[this._type.toLowerCase()];
            }

            this._mediaSource = mediaSource;

            mediaSource.prepare()
                .then(function () {
                    mediaPlayerState = mediaPlayer.getState();

                    self._mediaChangeInProgress = false;
                    if (self._deviceBrand === 'lg') {
                        self._resetMediaPlayer();

                        mediaPlayer.setMediaSource(mediaSource);
                    }

                    if (mediaPlayer.getState() === MediaPlayer.STATE.EMPTY) {
                        return;
                    }

                    if (Utils.isFunction(self._afterMediaChangeStuff)) {
                        self._afterMediaChangeStuff();
                        self._afterMediaChangeStuff = null;
                    }

                    if (isLive) {
                        mediaPlayer.beginPlayback({
                            async: true
                        });
                    } else if (isRestart) {

                        // Samsung live streams are 0 time based, so we don't need the relative streamtime.
                        if (self._delayedStreamTime && self._deviceBrand === 'samsung') {
                            mediaPlayer.beginLivePlayback({
                                async: true
                            });
                        } else if (self._deviceBrand === 'lg' && mediaPlayerState === MediaPlayer.STATE.EMPTY) {

                            // When restarting stream on LG from zero, TV is trying to seek 3 times
                            mediaPlayer.beginPlaybackFrom((self._program.getStartTime() + 1), {
                                async: true
                            });
                        } else {

                            if (!self._delayedStreamTime) {
                                self._delayedStreamTime = self._program.getStartTime() * 1000;
                            }

                            mediaPlayer.beginPlayback({
                                async: true
                            });
                        }

                    } else {

                        if (self._isPromo) {

                            // Dirt yuck yuck because Samsung.
                            mediaPlayer.beginLivePlayback();
                        } else if (isVodMovie) {
                            beginPlaybackFrom = contentRestarted || !self._bookmarkTime ? 0 : self._bookmarkTime;
                            mediaPlayer.beginPlaybackFrom(beginPlaybackFrom, {
                                async: true
                            });
                            self._vodContentRestarted = self._recordingContentRestarted = false;
                        } else {
                            beginPlaybackFrom = contentRestarted ? self._program.getStreamOffset() : (self._bookmarkTime ||
                                self._program.getStreamOffset());
                            mediaPlayer.beginPlaybackFrom(beginPlaybackFrom, {
                                async: true
                            });
                            self._vodContentRestarted = self._recordingContentRestarted = false;
                        }
                    }
                    self._restartStarted = false;
                    self._restartButtonPressed = false;

                    GA.onEvent('Action', EVENT.startVideo, {eventLabel: self._GATypeLabel});
                    graylog.onPlaybackStart(self._program._detailsAction, self._watchingVideoTimeStart);
                })
                ['catch'](Utils.bind(this._onPlayerError, this));
        },

        /**
         * Parse the bookmark object and retrieve the new starttime.
         *
         * @param {Object} data - The data.
         * @private
         */
        _parsedBookmarkTime: function (data) {
            var containers = Utils.getNested(data, 'resultObj', 'containers'),
                bookmarks;

            if (!Utils.isUndefined(containers) && containers.length) {
                bookmarks = Utils.getNested(containers[0], 'metadata', 'bookmarks');
            }

            if (!Utils.isUndefined(bookmarks) && bookmarks.length) {
                this._bookmarkTime = Utils.getNested(bookmarks[0], 'startDeltaTime');
            }
        },

        /**
         * Prepares the media source.
         *
         * @param {Object} params - The parameters.
         * @returns {Promise} - Promise resolving with the media source.
         * @private
         */
        _prepareMediaSource: function (params) {
            var headers = {},
                url; // browser hack to play video

            this._contentId = params.contentId;
            this._assetId = params.assetId;
            this._type = params.type;

            if (device.getModel() === 'webbbkit') {

                url = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4';
                return Promise.resolve(new MediaSource(url));
            }

            if (this._streamWithParental
                || (this._program && this._program.isLocked())
                || (this._program && this._program.streamRequiresPin())
            ) {
                if (sessionManager.getUserPin()) {
                    headers['pcPin'] = sessionManager.getUserPin();
                }

                this._streamWithParental = false;
            }

            if (params.type === TYPES.VOD || params.type === TYPES.VOD_MOVIE || params.type === TYPES.RECORDING) {
                return api.read('userdata', {
                    params: params,
                    withCredentials: true
                }).then(Utils.bind(function (data) {

                    // Check for empty assetId
                    params = this._checkForEmptyAsset(params, data);

                    this._parsedBookmarkTime(data);
                    return api.read('stream', {
                        params: params,
                        withCredentials: true,
                        headers: headers
                    });
                }, this));
            }

            return api.read('stream', {
                params: params,
                withCredentials: true,
                headers: headers
            });
        },

        /**
         * Check if there is an asset in the params.
         *
         * @param {Object} params - The _prepareMediaSource params.
         * @param {Object} data - The userdata response data.
         * @returns {Object} - The updated _prepareMediaSource params.
         *
         * @private
         */
        _checkForEmptyAsset: function (params, data) {
            var containers,
                assets,
                assetId,
                i;

            if (Utils.isUndefined(params.assetId)) {
                containers = Utils.getNested(data, 'resultObj', 'containers');

                if (!Utils.isUndefined(containers) && containers.length) {
                    assets = Utils.getNested(containers[0], 'entitlement', 'assets');

                    if (!Utils.isUndefined(assets) && assets.length) {

                        for (i = 0; i < assets.length; i++) {

                            if (assets[i].assetType === 'MASTER') {

                                if (assets[i].programType === 'CUTV') {
                                    assetId = assets[i].assetId;
                                    this._assetId = assetId;
                                    break;
                                }
                            }
                        }

                        if (!Utils.isEmpty(assetId)) {
                            params.assetId = assetId;
                        }
                    }
                }
            }

            return params;
        },

        /**
         * Creates and attaches the seeker.
         *
         * @private
         */
        _createAndAttachSeeker: function () {
            if (this._type !== TYPES.VOD && this._type !== TYPES.VOD_MOVIE && this._type !== TYPES.RECORDING) {
                this._buildLiveSeeker();
            } else {
                this._buildSeeker();
            }
        },

        /**
         * Sets the live progress.
         *
         * @param {booleand} [preventSeekingWhileLoading] - Prevent seeking on timeline while loading restart..
         * @private
         */
        _setLiveProgress: function (preventSeekingWhileLoading) {
            var program = this._program,
                startTime = new Date(program.getStartTime() * 1000),
                endTime = new Date(program.getEndTime() * 1000),
                progress,
                currentTime;

            progress = this._progress = ProgressManager.getLiveProgressPercentage(startTime, endTime, this._pauseTime);

            if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING && progress.behind < 0) {
                currentTime = application.getDate();
                this._pauseTime = currentTime.getTime() + progress.behind + 1000;
            }

            if (preventSeekingWhileLoading && !this._pauseTime && this._restartStarted) {
                return;
            }
            this._restartStarted = false;

            PlayerManager.setLivePlaybackPosition(this._pauseTime || application.getDate().getTime());

            if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
                if (this._seeker && typeof this._seeker.setCurrentTime === 'function') {
                    if (this._pauseTime) {
                        this._seeker.setCurrentTime(this._pauseTime);
                    } else {
                        this._seeker.setCurrentTime(application.getDate().getTime());
                    }
                }
            }

            if (progress.secondHead >= 100) {

                // First cancel the timeout.
                this._stopLiveProgressTimeout();

                // Retrieve the new program.
                this._getCurrentBroadcast(program.getChannelId())
                    .then(Utils.bind(function (epgItem) {
                        if (!epgItem) {

                            // Create epgData when missing.
                            epgItem = new EPGItem({}, channelManager.getChannelById(program.getChannelId()));
                        }

                        if (!sessionManager.getUserPin() && epgItem.isLocked()) {

                            if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
                                mediaPlayer.pause();
                            }

                            application.route('parentalpin', {
                                successCallback: Utils.bind(function () {
                                    this._checkingParental = false;
                                    this._playChannel(epgItem.getChannelId());
                                    application.broadcastEvent(new UnlockedEvent());
                                }, this),
                                errorCallback: Utils.bind(function (result) {
                                    this._checkingParental = false;

                                    if (Utils.isUndefined(result)) {
                                        this.routeToPage('channellist', program.getChannelId());
                                    }
                                }, this),
                                escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true),
                                callingPageKeyEvent: {
                                    keyCodes: [KeyEvent.VK_CHANNEL_UP, KeyEvent.VK_CHANNEL_DOWN],
                                    keyEventCallback: Utils.bind(this._skipCl, this, epgItem)
                                }
                            });
                        } else {
                            this._checkingParental = false;

                            // Set the new program data.
                            this._view.setProgram({program: epgItem, type: this._type});
                            this._program = epgItem;

                            if (this._view.getProgramDetails().hasClass('expand')) {
                                this._setDetailInfo();
                            }

                            // Restart the timeout.
                            this._startLiveProgressTimeOut();
                        }
                    }, this));
            }

            this._view.setLiveProgress(progress);
        },

        /**
         * Sets the live progress.
         *
         * @param {Date} fixedTime - Time when button was pressed.
         * @private
         */
        _setRestartProgress: function (fixedTime) {
            var program = this._program,
                startTime = new Date(program.getStartTime() * 1000),
                endTime = new Date(program.getEndTime() * 1000),
                currentTime = application.getDate(),
                behind = fixedTime - startTime,
                progress;

            this._pauseTime = currentTime.getTime() - behind;
            progress = this._progress = ProgressManager.getRestartProgressPercentage(
                startTime,
                endTime,
                behind);

            if (progress.secondHead >= 100) {

                // First cancel the timeout.
                this._stopRestartProgressTimeout();

                // Retrieve the new program.
                this._getCurrentBroadcast(program.getChannelId())
                    .then(Utils.bind(function (epgItem) {

                        // Set the new program data.
                        this._view.setProgram({program: epgItem, type: this._type});
                        this._program = epgItem;

                        // Restart the timeout.
                        this._startRestartProgressTimeOut(currentTime);
                    }, this));
            }

            this._view.setLiveProgress(progress);
        },

        /**
         * Sets the VOD progress.
         *
         * @param {Object} e - The player event.
         * @private
         */
        _setVODProgress: function (e) {
            var currentTime = e.currentTime,
                duration = e.duration,
                percentage = currentTime / duration * 100,
                view = this._view;

            view.setVODProgress({
                percentage: percentage,
                currentTime: Math.floor(currentTime),
                duration: Math.floor(duration)
            });

            // Execute callback once the progress has been set. Callback shows progressbar.
            if (this._playbackStatus.callback) {
                this._playbackStatus.callback();
                delete this._playbackStatus.callback;
            }

            if (this._bingewatch
                && this._bingewatch.length
                && !this._bingewatchActive
                && currentTime >= duration - 5) {
                this._bingewatchActive = true;
                view.hideUI({
                    skipAnim: true
                });
                application.route('bingewatching', {
                    episode: this._bingewatch.splice(0, 1)[0],
                    successCallback: Utils.bind(this._onBingeWatch, this),
                    cancelCallback: Utils.bind(this._onBack, this, true)
                });
            }
        },

        /**
         * Starts the live progress timeout.
         *
         * @private
         */
        _startLiveProgressTimeOut: function () {
            this._setLiveProgress();
            this._stopLiveProgressTimeout();

            this._liveProgressTimeout = setInterval(
                Utils.bind(this._setLiveProgress, this),
                LIVE_PROGRESS_TIMEOUT);
        },

        /**
         * Starts the live progress timeout.
         *
         * @param {Date} currentTime - Time when button was pressed.
         * @private
         */
        _startRestartProgressTimeOut: function (currentTime) {
            if (this._restartButtonPressed) {
                this._setRestartProgress(currentTime);
                this._restartButtonPressed = false;
            }
            this._liveProgressTimeout = setInterval(
                Utils.bind(this._setLiveProgress, this, true),
                LIVE_PROGRESS_TIMEOUT);
        },

        /**
         * Stops the live progress timeout.
         *
         * @private
         */
        _stopLiveProgressTimeout: function () {
            clearInterval(this._liveProgressTimeout);
        },

        /**
         * Stops the replay progress timeout.
         *
         * @private
         */
        _stopRestartProgressTimeout: function () {
            clearInterval(this._liveProgressTimeout);
        },

        /**
         * Sets the value of time to seeker.
         *
         * @private
         */
        _setCurrentTimeToSeeker: function () {
            var timeValue,
                program = this._program,
                seeker = this._seeker,
                currentTime = application.getDate();

            timeValue = currentTime.getTime() / 1000 - program.getStartTime();

            seeker.setCurrentTime(timeValue);
        },

        /**
         * KeyDown event for warm welcome overlay.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onKeyDownWarmWelcome: function (e) {
            var menu = application.getComponent('menu'),
                view = this._view,
                currentContent = this._warmWelcome.getActivePageId();

            this._warmWelcome.onKeyDown(e);

            if (currentContent === 'page-2') {
                if (!view.isUIVisible()) {
                    if (e.keyCode === KeyEvent.VK_DOWN) {
                        view.showUI(false, true);
                        this._warmWelcome.next();
                    }
                }
            } else if (currentContent === 'page-4') {
                if (menu.hasClass('invisible') || menu.hasClass('collapse')) {
                    if (e.keyCode === KeyEvent.VK_LEFT) {
                        application.showMenu('overlay', true);
                        this._warmWelcome.next();
                    }
                }
            }

            if (e.keyCode === KeyEvent.VK_RIGHT || e.keyCode === KeyEvent.VK_UP) {
                e.preventDefault();
                e.stopPropagation();
            }
        },

        /**
         * Select event for warm welcome overlay.
         *
         * @private
         */
        _onSelectWarmWelcome: function () {
            var currentContent = this._warmWelcome.getActivePageId(),
                view = this._view;

            if (currentContent === 'page-4') {
                view.hideUI();
            }
        },

        /**
         * KeyDown event.
         *
         * @param {Object} e - The event data.
         * @private
         * @returns {boolean} False if user input is blocked.
         */
        _onKeyDown: function (e) {
            var view = this._view,
                backButton = view.getBackButton(),
                controls = view.getControls(),
                showUI = false,
                miniEpgView;

            if (this._warmWelcome.isActive()) {
                this._onKeyDownWarmWelcome(e);
                e.preventDefault();
                e.stopPropagation();
                return;
            }

            if (application.blockUserInput) {
                e.preventDefault();
                e.stopPropagation();
                return false;
            }

            switch (e.keyCode) {
                case KeyEvent.VK_LEFT:
                    showUI = true;

                    if (this._callingPage !== 'landing') {
                        application.focusMenu('player');
                        view.closeExpandedContents();
                    }

                    e.stopPropagation();
                    break;

                case KeyEvent.VK_RIGHT:
                    e.stopPropagation();
                    break;

                case KeyEvent.VK_PAUSE:
                    this._onPause();
                    controls.focusPlayPauseButton();
                    break;

                case KeyEvent.VK_PLAY:
                    this._onPlay();
                    controls.focusPlayPauseButton();
                    break;

                case KeyEvent.VK_PLAY_PAUSE:
                    this._onPlayPause();
                    controls.focusPlayPauseButton();
                    break;

                case KeyEvent.VK_REWIND:
                    this._onRewind();
                    controls.focusRewindButton();
                    break;

                case KeyEvent.VK_FAST_FWD:
                    this._onFastForward();
                    controls.focusFastForwardButton();
                    break;

                case KeyEvent.VK_CHANNEL_UP:
                case KeyEvent.VK_CHANNEL_DOWN:
                    if (this._type === TYPES.LIVE || this._type === TYPES.RESTART) {
                        if (this._deviceBrand === 'samsung') {
                            this._setKeyHoldData(e, this._onKeyHoldSamsungHandler);
                        } else {
                            this._zapChannel(e);
                        }
                    }
                    break;

                case KeyEvent.VK_BACK:
                    if (this._view.isContentExpanded()) {
                        this._view.showUI(true);
                        this._view.closeExpandedContents();
                        controls.focus();
                    } else {
                        this._onBack();
                    }
                    break;

                case KeyEvent.VK_UP:
                    miniEpgView = this._view.getMiniEPG();

                    if (this._backButtonActive) {
                        view.hideUI();
                    }
                    if (miniEpgView.hasClass('expand') && miniEpgView.isFocussed()) {
                        this._view.showMiniEPG(true);
                        controls.focus();
                    } else {
                        backButton.focus();
                    }

                    break;

                case KeyEvent.VK_DOWN:

                    if (this._backButtonActive) {
                        controls.focus();
                    } else if (this._view.getMiniEPG().hasClass('expand')) {
                        this._view.getMiniEPG().focus();
                    } else {

                        if (this._view.isVisible()) {
                            this._view.showProgramInfo(false);
                            this._view.showMiniEPG(false);
                            this._view.getMiniEPG().focus();
                        }
                    }

                    view.showUI(true);

                    break;

                case KeyEvent.VK_0:
                case KeyEvent.VK_1:
                case KeyEvent.VK_2:
                case KeyEvent.VK_3:
                case KeyEvent.VK_4:
                case KeyEvent.VK_5:
                case KeyEvent.VK_6:
                case KeyEvent.VK_7:
                case KeyEvent.VK_8:
                case KeyEvent.VK_9:
                    this._switchChannelStart = application.getDate();
                    this._handleSwitchChannel(e);
                    break;
            }

            if (showUI && this._keys === '') {

                // UI disappears after 3 seconds (configurable) of no user input.
                view.showUI(true);
            }
        },

        /**
         * KeyUp event.
         *
         * @param {Object} e - The keyEvent data.
         * @private
         */
        _onKeyUp: function (e) {
            if (this._keyHoldTimeout) {
                this._onKeyHoldCancelled(e);
            }
        },

        /**
         * Sets the keyHold data.
         *
         * @param {Object} e - The keyEvent data.
         * @param {Function} keyHoldHandler - Function that handles the keyEvent.
         */
        _setKeyHoldData: function (e, keyHoldHandler) {
            var keyCode = e.keyCode;

            // Don't set keyHold while parental is being loaded. Prevents async triggers.
            if (this._checkingParental) {
                return;
            }
            this._keyHoldData = {};
            this._keyHoldData[keyCode] = {
                keyHoldHandler: Utils.bind(keyHoldHandler, this, e)
            };
            this._keyHoldTimeout = setTimeout(Utils.bind(this._onKeyHoldTriggered, this, e), KEY_HOLD_TIMEOUT);
        },

        /**
         * This function is triggered when keyHold times out. KeyHoldHandler executed with boolean meaning keyhold
         * behaviour should be executed.
         *
         * @param {Object} e - The keyEvent data.
         * @private
         **/
        _onKeyHoldTriggered: function (e) {
            var keyCode = e.keyCode,
                keyHoldData = this._keyHoldData[keyCode];

            this._keyHoldTimeout = null;

            if (keyHoldData && Utils.isFunction(keyHoldData.keyHoldHandler)) {
                keyHoldData.keyHoldHandler(true);
                delete this._keyHoldData[keyCode];
            }
        },

        /**
         * This function is triggered when keyHold is cancelled. KeyHoldHandler executed with boolean meaning keyhold
         * behaviour should not be executed.
         *
         * @param {Object} e - The keyEvent data.
         * @private
         **/
        _onKeyHoldCancelled: function (e) {
            var keyCode = e.keyCode,
                keyHoldData = this._keyHoldData[keyCode];

            clearTimeout(this._keyHoldTimeout);
            this._keyHoldTimeout = null;

            if (keyHoldData && Utils.isFunction(keyHoldData.keyHoldHandler)) {
                keyHoldData.keyHoldHandler(false);
                delete this._keyHoldData[keyCode];
            }
        },

        /**
         * KeyDown event for Samsung rc.
         *
         * @param {Object} e - The event data.
         * @param {boolean} keyHoldTriggered - True if triggered by keyHold timeout or cancel by keyup event.
         * @private
         */
        _onKeyHoldSamsungHandler: function (e, keyHoldTriggered) {

            switch (e.keyCode) {
                case KeyEvent.VK_CHANNEL_UP:
                case KeyEvent.VK_CHANNEL_DOWN:
                    if (keyHoldTriggered && !this._checkingParental) {

                        this._view.showMiniEPG();
                        if (!this._view.getMiniEPG().hasClass('expand')) {
                            this._view.getControls().focus();
                        }
                    } else {
                        this._zapChannel(e);
                    }
                    break;
            }
        },

        /**
         * KeyDown event for controls.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onControlKeyDown: function (e) {
            var view = this._view,
                shouldHide = true,
                controls = view.getControls();

            if (controls.isVisible() &&
                (e.keyCode === KeyEvent.VK_LEFT || e.keyCode === KeyEvent.VK_RIGHT)) {

                if (mediaPlayer.getState() === MediaPlayer.STATE.PAUSED) {
                    shouldHide = false;
                }

                view.showUI(shouldHide);
            }
        },

        /**
         * Focus event.
         *
         * @param {Object} e - The event data.
         *
         * @private
         */
        _onFocus: function (e) {
            var target = e.target,
                main = application.getComponent('main'),
                activeMainWidget = main.getActiveChildWidget(),
                landingPage = activeMainWidget ? activeMainWidget.getId() === 'landingPage' : false;

            this._backButtonActive = target.id === 'back-button';

            if (this._showErrorOnPlayerFocus === true) {
                this._cannotLoadVideoError();
            } else {
                if (!this._warmWelcome.isActive() && !landingPage) {
                    application.getComponent('player').setStyleTo('display', 'block');
                    application.hideMenu('player');
                }
            }
        },

        /**
         * Resets the player.
         *
         * @param {boolean} [fromSeek] - From seek.
         * @param {boolean} [fromChannelSwitch] - From channel switch.
         * @param {boolean} [initialReset] - Initial reset.
         * @private
         */
        _resetPlayer: function (fromSeek, fromChannelSwitch, initialReset) {
            var mediaPlayerState = mediaPlayer.getState(),
                seeker = this._seeker;

            this._deferredPlayerInteraction = false;
            this._playbackStatus.active = false;

            if (seeker) {

                if (seeker.isActive()) {
                    seeker.cancel();
                }

                seeker.detach();
                this._seeker = null;
            }

            if (!fromSeek) {
                this._delayedStreamTime = null;
                this._streamStart = null;
                this._pauseTime = null;
            }

            this._errorState = false;
            this._isPromo = false;
            this._progress = null;
            this._bookmarkTime = null;
            this._view.resetControls();
            this._view.hideWarningBox(true);
            this._keys = '';
            this._view.resetLiveLabels();
            this._view.getZappbanner().resetPauseLabel();

            if (fromChannelSwitch) {
                BookmarkManager.onStop(this._contentId);
            } else {
                if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
                    if (mediaPlayer.getCurrentTime() / mediaPlayer.getDuration() < configuration.BOOKMARK_THRESHOLD) {
                        BookmarkManager.onStop(null, mediaPlayer.getCurrentTime());
                    } else {
                        BookmarkManager.onStop(null, 0);
                    }
                } else {
                    BookmarkManager.onStop();
                }
            }

            if (mediaPlayerState !== MediaPlayer.STATE.STOPPED
                && mediaPlayerState !== MediaPlayer.STATE.EMPTY
                && mediaPlayerState !== MediaPlayer.STATE.ERROR) {
                mediaPlayer.stop();
            }

            this._view.onPlay();
            mediaPlayerState = mediaPlayer.getState();

            if (mediaPlayerState !== MediaPlayer.STATE.EMPTY) {
                mediaPlayer.reset();
                if (!initialReset) {
                    this._mediaChangeInProgress = true;
                }
            }

            this._stopLiveProgressTimeout();
            this._stopRestartProgressTimeout();
        },


        /**
         * Checks whether broadcast is parental protected and if yes, starts parental flow.
         *
         * @param {string} channelId - The channel id.
         * @param {string} assetId - The asset id.
         * @returns {Promise} - Promise resolving with the channel data.
         * @private
         */
        _checkChannelParental: function (channelId, assetId) {

            return this._getCurrentBroadcast(channelId, assetId)
                .then(Utils.bind(function (broadcast) {

                    if (!broadcast) {
                        broadcast = EPGItem({}, channelManager.getChannelById(channelId));
                    }

                    if (broadcast.isLocked()) {
                        this._streamWithParental = true;
                    }
                    return new Promise(Utils.bind(function (resolve, reject) {

                        if (!sessionManager.getUserPin() && broadcast.isLocked()) {

                            application.route('parentalpin', {
                                successCallback: Utils.bind(function () {
                                    this._program = broadcast;
                                    this._checkingParental = false;
                                    resolve();
                                }, this),
                                errorCallback: reject, // ParentalHelper.parentaleErrorCallback.bind(this),
                                escapeCallback: Utils.bind(ParentalHelper.parentaleEscapeCallback, this),
                                callingPageKeyEvent: {
                                    keyCodes: [KeyEvent.VK_CHANNEL_UP, KeyEvent.VK_CHANNEL_DOWN],
                                    keyEventCallback: Utils.bind(this._skipCl, this, broadcast)
                                }
                            });
                        } else {
                            this._program = broadcast;
                            this._checkingParental = false;
                            resolve();
                        }

                    }, this));

                }, this));

        },

        /**
         * Skips child lock from current locked channel.
         *
         * @param {Object} broadcast - The current program.
         * @param {Object} ev - Key event.
         * @private
         */
        _skipCl: function (broadcast, ev) {
            this._program = broadcast;
            this._checkingParental = false;
            this._zapChannel(ev);
        },

        /**
         * Zaps next or previous channel depending on key event.
         *
         * @param {Object} ev - Key Event.
         */
        _zapChannel: function (ev) {
            var channelToSwitch;

            if (this._type === TYPES.LIVE || this._type === TYPES.RESTART) {
                if (ev.keyCode === KeyEvent.VK_CHANNEL_UP) {
                    channelToSwitch = channelManager.getNextChannel(this._program.getChannel());
                } else {
                    channelToSwitch = channelManager.getPreviousChannel(this._program.getChannel());
                }

                this._switchChannelStart = application.getDate();

                this._switchChannel(channelToSwitch);
            }
        },

        /**
         * Attempts to switch to the new channel.
         *
         * @param {Object} channel - The new channel.
         * @private
         */
        _switchChannel: function (channel) {
            var self = this,
                channelId = channel.getId(),
                assetId = channel.getAssetId(),
                promise,
                main = application.getComponent('main'),
                detail = application.getComponent('detail'),
                menu = application.getComponent('menu'),
                controls;

            if (this._callingPage) {

                if (this._callingPage === 'detail') {
                    detail.setStyleTo('display', '');
                    detail._historyStack = [];
                    detail.back();
                }

                if (this._callingPage !== 'channelList') {
                    this._callingPage = null;

                    main.setStyleTo('display', '');
                    main._historyStack = [];
                    main.back();
                }

                menu.getChildWidgetByIndex(0).setSelectedIndex({section: 'MIDDLE', index: 0});
                this.focus();
            }

            if (!this._canSwitchChannel() || !this._previousChannelResolved || this._checkingParental) {
                return;
            }

            this._checkingParental = true;
            this._checkChannelParental(channelId, assetId).then(Utils.bind(function () {

                this._inputTimeout = null;
                this._resetPlayer(false, true);

                // Save last played channel id
                channelManager.setLastWatchedChannel(channelId);

                this._channelPromise = this._loadNextChannelMediaSource(channelId, assetId);
                this._lastChannelId = channelId;

                this._previousChannelResolved = false;
                this._checkingParental = false;

                clearTimeout(this._channelTimer);

                // Close any expanded content before channel switch.
                if (this._view.isContentExpanded()) {
                    controls = this._view.getControls();

                    this._view.closeExpandedContents();
                    controls.focus();
                }

                // todo append this into a stack of promises and resolve only when processed all
                promise = Utils.clone(this._channelPromise
                    .then(function (results) {
                        self._onChannelUI(results);

                        // render next channel UI first but play only the last promise in the stack
                        if (promise === (promiseStack[promiseStack.length - 1])) {

                            // player timeout to avoid UI get stucked
                            self._channelTimer = setTimeout(function () {
                                if (mediaPlayer.getState() !== MediaPlayer.STATE.EMPTY) {
                                    self._resetPlayer();
                                }
                                self._playMediaSource(results[0]);
                                self._previousChannelResolved = true;

                                GA.onEvent('Action', EVENT.stopVideo, {eventLabel: self._GATypeLabel});
                            }, 0);

                            promiseStack = [];
                        }
                    }))
                    ['catch'](function () {

                        /*
                         * When player going to error state while using DirectDial,
                         * set previousChannelResolved to true, otherwise cannot use direct dial anymore.
                         */
                        self._previousChannelResolved = true;
                        self._checkingParental = false;

                        self._onPlayerError();
                });

                promiseStack.push(promise);
                zapping = true;

            }, this)).catch(Utils.bind(function () {
                this._keys = '';
                this._inputTimeout = null;
                this._checkingParental = false;
                this._view.getChannelSwitcher().setLabel('');
            }, this));
        },

        /**
         * Attempts to load channel data.
         *
         * @param {string} channelId - The channel id.
         * @param {string} assetId - The asset id.
         * @returns {Promise} - Promise resolving with the channel data.
         * @private
         */
        _loadChannelData: function (channelId, assetId) {

            // Only show the loader if WarmWelcome is not active.
            if (!this._warmWelcome.isActive()) {
                application.showLoader(true, true);
            }

            return this._getCurrentBroadcast(channelId, assetId)
                .then(Utils.bind(function (broadcast) {

                    // create epgData when missing.
                    if (!broadcast) {
                        broadcast = new EPGItem({}, channelManager.getChannelById(channelId));
                    }
                    this._program = broadcast;

                    if (broadcast.isLocked() && !sessionManager.getUserPin()) {
                        return Promise.resolve([
                            'locked',
                            broadcast
                        ]);
                    }

                    return this._prepareMediaSource({
                        contentId: channelId,
                        assetId: assetId,
                        type: 'LIVE'
                    })
                        .then(function (mediaSource) {
                            return [
                                mediaSource,
                                broadcast
                            ];
                        });
                }, this));
        },

        /**
         * Attempts to load media source for given channel and asset.
         *
         * @param {string} channelId - The channel id.
         * @param {string} assetId - The asset id.
         * @returns {Promise} - Promise resolving with the channel data.
         * @private
         */
        _loadNextChannelMediaSource: function (channelId, assetId) {
            var broadcast = this._program;

            return this._prepareMediaSource({
                contentId: channelId,
                assetId: assetId,
                type: 'LIVE'
            }).then(function (mediaSource) {
                return [
                    mediaSource,
                    broadcast
                ];
            });
        },

        /**
         * Returns the current broadcast.
         *
         * @param {string} channelId - The channel id.
         * @returns {Promise} - Promise resolving with the broadcast.
         * @private
         */
        _getCurrentBroadcast: function (channelId) {
            if (this._pauseTime) {

                return channelManager.getBroadcastAtTime(
                    channelId,
                    application.getDate() - Math.abs(this._progress.behind)
                );
            }

            return channelManager.getCurrentBroadcastForChannel(channelId);
        },

        /**
         * Removes the tizen multitask handler.
         */
        _removeTizenMultitaskHandler: function removeTizenMultitaskHandlerFn () {

            document.removeEventListener('visibilitychange', this._multitaskHandler);
        },

        /**
         * Sets the tizen multitask handler.
         */
        _setTizenMultitaskHandler: function setTizenMultitaskHandlerFn () {

            document.addEventListener('visibilitychange', this._multitaskHandler);
        },

        /**
         * Visibility changed event.
         *
         * @private
         */
        _onVisibilityChanged: function () {
            if (!document.hidden) {

                if (sessionManager.isLoggedIn()) {
                    sessionManager.refreshToken()
                        .then(Utils.bind(function () {
                            this._callingPage = null;
                            this._lastChannelId = null;
                            this._resetPlayer();
                            this._playChannel(null, true);
                    }, this));
                }
            }
        },

        /**
         * Returns true if the channel can be switched.
         *
         * @returns {boolean} - True if the channel can be switched.
         * @private
         */
        _canSwitchChannel: function () {
            return this._type === TYPES.LIVE || this._type === TYPES.RESTART;
        },

        /**
         * Gets executed when the seek is confirmed.
         *
         * @param {Object} e - The seek event data.
         * @private
         */
        _onSeek: function (e) {
            this._seekSpeed = null;
            this._seekCurrentTime = null;

            this._view.resetControls();
            this._view.getZappbanner().setTrickplaySettings();

            if (this._type !== TYPES.VOD && this._type !== TYPES.VOD_MOVIE && this._type !== TYPES.RECORDING) {
                this._startLiveProgressTimeOut();
            }

            // Below only gets executed for live trickplay.
            if (e && e.seekTo && this._type === TYPES.LIVE) {
                if (e.currentTime >= application.getDate()) {
                    this._resetPlayer();
                    this._playChannel(this._program.getChannelId());
                } else {
                    this._pauseTime = e.currentTime;
                    this._delayedStreamTime = e.currentTime;
                    this._resetPlayer(true);
                    this._onRestart();
                }

            } else if (this._type === TYPES.RESTART) {

                if (e.currentTime >= application.getDate()) {
                    this._resetPlayer();
                    this._playChannel(this._program.getChannelId());
                } else {
                    mediaPlayer.resume();

                    if (e.relativePlaybackTime === 0) {
                        this._pauseTime = this._program.getStartTime() * 1000;
                        if (this._deviceBrand === 'lg') {
                            mediaPlayer.playFrom(this._program.getStartTime() + 1);
                        } else {
                            mediaPlayer.playFrom(0);
                        }
                    } else {
                        this._pauseTime = e.currentTime;
                        mediaPlayer.playFrom(e.seekTo);
                    }
                }
            }
        },

        /**
         * Gets executed when the current time changes.
         *
         * @param {number|Object} data - The seek data.
         * @private
         */
        _onSeekCurrentTimeChanged: function (data) {

            if (!this._seeker.isActive()) {
                return;
            }

            if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
                this._seekCurrentTime = data;
                this._onSeekVODTime(data);
            } else {
                this._onSeekLIVETime(data);
            }
        },

        /**
         * Gets executed when the current seek time changes for live video.
         *
         * @param {Object} data - The data.
         * @param {number} data.currentTime - The current seek time.
         * @param {number} data.relativePlaybackTime - The current relative playback time.
         *
         * @private
         */
        _onSeekLIVETime: function (data) {
            var programStart = this._program.getStartTime() * 1000,
                seeker = this._seeker,
                currentSeekTime = data.currentTime,
                maxSeekTime = PlayerManager.getMaxSeekValue() * 1000,
                playFrom;

            if (currentSeekTime < programStart) {

                if (this._type === TYPES.LIVE) {
                    this._resetPlayer();
                    this._pauseTime = this._delayedStreamTime = programStart;
                    this._onRestart();
                } else {
                    seeker.confirm(programStart);
                }
                return;
            }

            if (currentSeekTime >= application.getDate()) {

                /*
                 * Intentionally set the playfrom time higher than the current date.
                 * This way the seeker will think it's past the live moment and start playing from live.
                 */
                playFrom = application.getDate();
                playFrom.setMinutes(playFrom.getMinutes() + 1);
                seeker.setCurrentTime(playFrom.getTime());
                seeker.confirm(playFrom.getTime());
            }

            if (!this._program.canSeek()
                && seeker.getDirection() === 1
                && data.relativePlaybackTime > maxSeekTime) {
                seeker.confirm(programStart + maxSeekTime);
                return;
            }

            this._pauseTime = currentSeekTime;

            this._setLiveProgress();
        },

        /**
         * Gets executed when the current seek time changes for VOD.
         *
         * @param {number} time - The current seek time.
         * @private
         */
        _onSeekVODTime: function (time) {
            var duration = mediaPlayer.getDuration(),
                maxTime = PlayerManager.getMaxSeekValue();

            if (!this._program.canSeek() && this._seekSpeed > 0 && time >= maxTime) {
                this._seeker.confirm(maxTime);
                time = maxTime;
            }

            if (time <= 0) {
                this._seeker.confirm();
            } else if (time >= duration) {
                this._seeker.confirm(duration - 5);
            }

            this._setVODProgress({
                currentTime: time,
                duration: duration
            });
        },

        /**
         * Gets executed when the speed changes.
         *
         * @param {number} speed - The speed.
         * @private
         */
        _onSeekSpeedChanged: function (speed) {
            this._seekSpeed = speed;

            if (speed) {
                this._view.updateSeekSpeed(speed);
            }
        },

        /**
         * Returns true if the UI can be shown.
         *
         * @returns {boolean} - True if the UI can be shown.
         * @private
         */
        _canShowUI: function () {
            return !this._warmWelcome.isActive();
        },

        /**
         * Returns the value of playbackStatus property.
         *
         * @returns {Object} - Container properties regarding the current playback state.
         */
        getPlaybackStatus: function () {
            return this._playbackStatus;
        },

        /**
         * Gets executed when the player error triggers.
         *
         * @param {Object} e - The player error event.
         * @private
         */
        _onPlayerError: function (e) {
            var message;

            if (this._errorState) {
                return;
            }

            this._errorState = true;

            application.hideLoader();

            if (e && e.resultCode && e.resultCode === '406') {
                this._onPlaybackIssueMessage(e);
                return;
            }

            if (e && e.toString && (e.toString().indexOf('PLAYER_ERROR_INVALID_STATE') >= 0)) {

                /*
                 * We want to ignore this error from Samsung Tizen,
                 * which is caused when zapping fast.
                 */
                return;
            }

            if (e) {
                message = e.toString ? e.toString() : JSON.stringify(e);
                if (this._mediaSource) {
                    Graylog.getInstance().onPlaybackError(this._mediaSource.getMediaUrl(), 'n.a.', 'playready', message);
                } else {
                    Graylog.getInstance().onPlaybackError('n.a', 'n.a', 'n.a.', message);
                }
            }

            if (this._deviceBrand === 'default') {

                // We want to disable the player errors for the development browser.
                return;
            }

            if (this._warmWelcome.isActive()) {
                this._view.hideUI();
            }

            if (application.getComponent('player').isFocussed()) {
                this._cannotLoadVideoError(Utils.getNested(e, 'errorDescription') || '');
            } else {
                this._showErrorOnPlayerFocus = true;
            }
        },

        /**
         * Focus.
         */
        focus: function () {
            if (this.outputElement.style.display === 'none') {
                this.setStyleTo('display', 'block');
            }

            if (this._warmWelcome.isActive()) {
                this._warmWelcome.focus();
            } else {
                this._view.focus();
            }

            if (this._triggerParentalOnFocus) {
                application.route('parentalpin', {
                    successCallback: Utils.bind(function () {
                        this.onBeforeShow({
                            args: this._lastPlayerArguments
                        });
                        application.broadcastEvent(new UnlockedEvent());
                    }, this),
                    errorCallback: Utils.bind(function (result) {

                        if (Utils.isUndefined(result)) {
                            this.routeToPage('channellist');
                        }
                    }, this),
                    escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true)
                });
            }
        },

        /**
         * Reports the bitrate change.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _reportBitrateChange: function (e) {

            GA.onEvent('player', 'bitrate', {
                eventLabel: e.bitrate
            });
        },

        /**
         * Return the current player view.
         *
         * @returns {Object} - The current player view.
         */
        getView: function () {
            return this._view;
        },

        /**
         * Hides the warning box.
         *
         * @param {boolean} [skipAnim] - True if should skip the animations.
         */
        hideWarningBox: function (skipAnim) {
            this._warningBox.hide({
                skipAnim: skipAnim
            });
        },

        /**
         * Shows the warning box.
         *
         * @param {Object} config - The config.
         */
        showWarningBox: function (config) {
            this._warningBox.show(config);
        },

        /**
         * Popups the error message.
         *
         * @param {string} errorCode - The errorcode.
         *
         * @private
         */
        _cannotLoadVideoError: function (errorCode) {
            var self = this,
                type = 'fullscreen',
                imgUrl = 'src/assets/images/error-icon.png',
                title,
                text,
                button;

            errorCode = errorCode || '';

            switch (errorCode) {
                case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_1:
                case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_2:
                case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_3:
                case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_4:
                    title = L10N.getInstance().get('errors.stream.max_concurrent_streams_title');
                    text = L10N.getInstance().get('errors.stream.max_concurrent_streams_text');
                    button = {id: 'error-close-button', label: L10N.getInstance().get('errors.ok')};
                    break;
                default:
                    title = L10N.getInstance().get('errors.cannot_load_video');
                    button = {id: 'error-close-button', label: L10N.getInstance().get('errors.close')};
            }

            this._showErrorOnPlayerFocus = false;
            application.hideLoader();
            application.route('error', {
                type: type,
                title: title,
                text: text,
                button: button,
                imgUrl: imgUrl,
                errorCode: errorCode,
                callback: function () {
                    self._resetPlayer();
                    self._playChannel(null, true);
                    self._onBack();
                }
            });
        },

        /**
         * Gets called when a mouse seek event happens.
         *
         * @param {Object} e - The event data.
         * @param {number} e.percentage - The seek percentage.
         * @param {boolean} e.finished - True if the mouse seek event has finished.
         * @private
         */
        _onMouseSeekEvent: function (e) {
            var type = this._type;

            if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
                mediaPlayer.pause();
            }

            if (type === TYPES.VOD || type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
                this._onVODMouseSeek(e);
            } else {
                this._onLiveRestartMouseSeek(e);
            }
        },

        /**
         * Determine direction of mouse seekening.
         *
         * @param {number} currentTime - Current time of the stream.
         * @param {number} newTime - New time of the stream that we want to play.
         * @returns {string} - Direction of the seeking.
         * @private
         */
        _getMouseSeekDirection: function (currentTime, newTime) {
            var direction;

            if (currentTime > newTime) {
                direction = DIRECTION.backward;
            } else {
                direction = DIRECTION.forward;
            }

            return direction;
        },

        /**
         * VOD Mouse Seek event.
         *
         * @param {Object} e - The event data.
         * @param {number} e.percentage - The seek percentage.
         * @param {boolean} e.finished - True if the mouse seek event has finished.
         * @private
         */
        _onVODMouseSeek: function (e) {
            var percentage = e.percentage,
                duration,
                currentTime;

            duration = mediaPlayer.getDuration();
            currentTime = (duration * percentage) / 100;

            if (!this._prevCurrentTime) {
                this._prevCurrentTime = currentTime;
            }

            this._view.setVODProgress({
                percentage: percentage,
                currentTime: Math.floor(currentTime),
                duration: Math.floor(duration)
            });

            if (e.finished) {

                if (this._getMouseSeekDirection(this._prevCurrentTime, currentTime) === DIRECTION.backward) {
                    GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.backward});
                } else {
                    GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.forward});
                }

                this._prevCurrentTime = currentTime;
                this._seeker.confirm(currentTime);
            }
        },

        /**
         * Live and Restart Mouse Seek event.
         *
         * @param {Object} e - The event data.
         * @param {number} e.percentage - The seek percentage.
         * @param {boolean} e.finished - True if the mouse seek event has finished.
         * @private
         */
        _onLiveRestartMouseSeek: function (e) {
            var start = this._program.getStartTime() * 1000,
                end = this._program.getEndTime() * 1000,
                seeker = this._seeker,
                duration = end - start,
                difference = (duration / 100) * (100 - e.percentage),
                seekTo = end - difference,
                playFrom;

            this._pauseTime = Math.round(seekTo);
            this._setLiveProgress();

            if (!this._prevProgress) {
                this._prevProgress = e.percentage;
            }

            if (e.finished) {

                if (this._getMouseSeekDirection(this._prevProgress, e.percentage) === DIRECTION.backward) {
                    GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.backward});
                } else {
                    GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.forward});
                }

                this._prevProgress = e.percentage;

                if (seekTo < start) {

                    if (this._type === TYPES.LIVE) {
                        this._pauseTime = start;
                        this._resetPlayer();
                        this._onRestart();
                    } else {
                        seeker.confirm(start);
                    }
                    return;
                }

                // Because of timing add 10 seconds to this check.
                if ((seekTo + 10000) >= application.getDate()) {

                    /*
                     * Intentionally set the playfrom time higher than the current date.
                     * This way the seeker will think it's past the live moment and start playing from live.
                     */
                    playFrom = application.getDate();
                    playFrom.setMinutes(playFrom.getMinutes() + 1);
                    seeker.setCurrentTime(playFrom);
                    seeker.confirm(playFrom);
                } else {

                    seeker.confirm(seekTo);
                }
            }
        },

        /**
         * Executes a delayed seek.
         *
         * @private
         */
        _delayedSeek: function () {
            var delayedTime = this._delayedStreamTime,
                startTime = this._program.getStartTime() * 1000,
                currentTime = mediaPlayer.getCurrentTime(),
                seekTo;

            this._pauseTime = delayedTime;

            if (this._deviceBrand === 'samsung') {
                seekTo = (delayedTime / 1000) - (startTime / 1000);
            } else {
                seekTo = currentTime + ((delayedTime / 1000) - (startTime / 1000));
            }

            this._delayedStreamTime = null;
            mediaPlayer.playFrom(Math.round(seekTo));
        },

        /**
         * Behaviour when go back from player.
         *
         * @param {boolean} [skipBingewatch] - True if bingewatch popup should be skipped.
         * @private
         */
        _onBack: function (skipBingewatch) {
            var main = application.getComponent('main'),
                detail = application.getComponent('detail'),
                landingPage = main.getChildWidget('landingPage'),
                view = this._view,
                self = this,
                percentage = (100 / mediaPlayer.getDuration()) * mediaPlayer.getCurrentTime(),
                changeMedia = function () {
                    self._resetPlayer();
                    self._deferredPlayerInteraction = true;
                    self._playChannel(self._lastChannelId);
                    application.hideLoader(2000);
                };

            if (this._bingewatchActive && !skipBingewatch) {
                return;
            }

            if (this._view.isContentExpanded()) {
                this._view.closeExpandedContents();
            }

            if (!skipBingewatch
                && this._bingewatch
                && this._bingewatch.length
                && percentage > 95
                && !this._bingewatchActive) {
                this._bingewatchActive = true;
                view.hideUI({
                    skipAnim: true
                });
                application.route('bingewatching', {
                    episode: this._bingewatch.splice(0, 1)[0],
                    successCallback: Utils.bind(this._onBingeWatch, this),
                    cancelCallback: Utils.bind(this._onBack, this, true)
                });
                return;
            }

            this._bingewatchActive = false;
            application.showLoader(true, true);
            GA.onEvent('Action', EVENT.stopVideo, {eventLabel: self._GATypeLabel});

            if (view.isUIVisible()) {
                view.hideUI({
                    skipAnim: true
                });
                view.hideWarningBox(true);
            }

            if (this._callingPage === 'detail' || this._callingPage === 'movies') {

                if (main.getContent()) {
                    this.setStyleTo('display', 'none');
                    main.setStyleTo('display', 'block');
                    application.showMenu(this._callingPage, false, true);

                    if (this._callingPage === 'detail') {
                        this._callingPage = null;
                        detail.setStyleTo('display', 'block');
                        detail.focus();
                    } else {
                        main.focus();
                    }
                } else {
                    application.focusMenu('player');
                }

                if (this._type === TYPES.RECORDING || this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE) {
                    if (this._mediaChangeInProgress) {
                        this._afterMediaChangeStuff = changeMedia;
                    } else {
                        changeMedia();
                    }
                } else {
                    application.hideLoader();
                }

            } else if (this._callingPage === 'landing') {
                this._callingPage = null;

                if (this._seeker.isActive()) {
                    this._seeker.cancel();
                }
                this._removeEventListeners();
                landingPage.togglePlayers(false);

            } else if (this._callingPage === 'channelList') {

                if (this._lastChannelId) {
                    this.routeToPage('channellist', this._lastChannelId);
                }

                this._callingPage = null;

                this.setStyleTo('display', 'none');
                main.setStyleTo('display', 'block');
                main.focus();
            } else {
                application.focusMenu('player');
                application.hideLoader();
            }
        },

        /**
         * Shorthand for resetting the device mediaplayer.
         *
         * @private
         */
        _resetMediaPlayer: function () {
            var mediaPlayerState = mediaPlayer.getState();

            if (mediaPlayerState !== MediaPlayer.STATE.STOPPED
                && mediaPlayerState !== MediaPlayer.STATE.EMPTY
                && mediaPlayerState !== MediaPlayer.STATE.ERROR) {
                mediaPlayer.stop();
            }

            mediaPlayerState = mediaPlayer.getState();

            if (mediaPlayerState !== MediaPlayer.STATE.EMPTY) {
                mediaPlayer.reset();
            }
        },

        /**
         * Playback issue message displayer.
         *
         * @private
         */
        _onPlaybackIssueMessage: function () {

            this._view.showWarningBox({
                icon: 'icon-alert-v2',
                text: l10n.get('player.warningbox.cantplay')
            });

            GA.onEvent('playback_failed', 'player');
        },

        /**
         * Bingewatch callback.
         *
         * @param {Object} episode - The episode to watch.
         * @private
         */
        _onBingeWatch: function (episode) {
            application.broadcastEvent(new BingeWatchEvent(episode));
            this._bingewatchActive = false;
            this._view.closeExpandedContents();
            this.onBeforeShow({
                args: {
                    data: episode,
                    bingewatch: this._bingewatch,
                    callingPage: this._callingPage,
                    analyticsType: this._analyticsType,
                    type: this._type
                }
            });
        },

        /**
         * Attempts to play the next episode.
         *
         * @param {Object} episode - The episode to watch.
         **/
        _playNextEpisode: function (episode) {
            application.broadcastEvent(new NextEpisodeEvent(episode));
            this.onBeforeShow({
                args: {
                    data: episode,
                    bingewatch: this._nextEpisodes,
                    callingPage: this._callingPage,
                    analyticsType: this._analyticsType,
                    type: this._type
                }
            });
        },

        /**
         * Reactivates the player.
         */
        reactivate: function () {
            var view = this._view;

            view.enablePointerSupport();
            view.focus();

            this.routeToPage('home');
            view.removeClass('warm-welcome');
            this._playChannel();
        },

        /**
         * On parental control event changed.
         *
         * @param {Object} e - Parental control event.
         * @param {boolean} e.status - Parental control status (on or off).
         */
        _onParentalControlChangedEvent: function (e) {
            var parentalControlOn = e.status;

            if (parentalControlOn && this._program && this._program.isLocked()) {
                if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
                    mediaPlayer.pause();
                }

                this._lastChannelId = null;
                this._triggerParentalOnFocus = parentalControlOn;
            }
        },

        /**
         * Requests and sets the content's detail info.
         *
         * @private
         */
        _setDetailInfo: function () {
            api.read('detail', {
                params: {
                    endpoint: this._program.getDetailsAction()
                },
                withCredentials: true
            }).then(Utils.bind(this._onInfo, this, true));
        }
    });

    return PlayerComponent;
});