Source: models/broadcast.js

define('application/models/broadcast', [
    'antie/class',
    'application/managers/channel',
    'application/managers/api',
    'antie/runtimecontext',
    'rofl/lib/utils',
    'application/models/relatedcontent',
    'application/managers/session',
    'rofl/lib/l10n',
    'antie/storageprovider',
    'application/managers/feature'
], function (
    Abstract,
    ChannelManager,
    ApiManager,
    RuntimeContext,
    Utils,
    RelatedContent,
    SessionManager,
    L10N,
    StorageProvider,
    FeatureManager
) {
    'use strict';

    var channelManager = ChannelManager.getInstance(),
        app = RuntimeContext.getCurrentApplication(),
        imageFormat = app.getLayout().imageFormat,
        landscapeFormat = imageFormat.landscape,
        getDetailsAction = function (actions) {
            var i,
                j,
                detailsAction;

            for (i = 0, j = actions.length; i < j; i++) {

                if (actions[i].targetType === 'DETAILS_PAGE') {
                    detailsAction = actions[i].uri;
                    break;
                }
            }

            return detailsAction;
        },
        l10n = L10N.getInstance(),
        CONTENT_OPTIONS = {
            TRICKPLAY: 'TRICKPLAY',
            STARTOVER: 'STARTOVER',
            CATCHUP: 'CATCHUP',
            RECORDABLE: 'RECORDABLE'
        },
        Broadcast,
        imageEndpoint;

    Broadcast = Abstract.extend({

        /**
         * Initialises the search result model from API response object.
         *
         * @param {Object} response - API search result object.
         */
        init: function (response) {
            var metadata = response.metadata;

            if (metadata.liveContentId) {
                this._id = parseInt(metadata.liveContentId);
                this._recordingId = parseInt(metadata.contentId);
            } else {
                this._id = parseInt(response.id);
            }

            this._title = metadata.title;
            this._titleBrief = metadata.titleBrief;
            this._imageUrl = metadata.pictureUrl;
            this._channelId = metadata.channelId || Utils.getNested(response, 'channel', 'channelId');
            this._assetId = this._getMasterAssetId(response.assets);

            if (metadata.airingStartTime) {
                this._startTime = (metadata.airingStartTime / 1000);
                this._endTime = metadata.airingEndTime / 1000;
                this._streamOffset = 5 * 60; // Hardcode the offset to 5 minutes.
            } else if (metadata.promoStartTime) {
                this._startTime = (metadata.promoStartTime / 1000);
                this._endTime = metadata.promoEndTime / 1000;
                this._streamOffset = 0;
            } else {
                this._startTime = (metadata.programStartTime / 1000);
                this._endTime = (metadata.programStartTime / 1000) + metadata.programDuration;
                this._streamOffset = (metadata.programStartTime - metadata.recordingStartTime) / 1000;
            }

            this._assets = response.assets;
            this._duration = metadata.programDuration || metadata.duration;
            this._catchupDuration = this._recordingDuration = metadata.duration || metadata.programDuration;
            this._videoRatings = metadata.pcExtendedRatings;
            this._detailsAction = getDetailsAction(response.actions || []);
            this._description = metadata.longDescription || '';
            this._ageRating = 'PC-' + metadata.pcLevel;
            this._genres = metadata.genres || [];
            this._contentOptions = metadata.contentOptions;
            this._programType = metadata.programType || null;
            this._episodeNumber = metadata.episodeNumber;
            this._seriesId = parseInt(metadata.seriesId);
            this._seriesTitle = metadata.seriesTitle;
            this._season = metadata.season;
            this._contentType = metadata.contentType;
            this._contentSubtype = metadata.contentSubtype;
            this._startDeltaTime = metadata.startDeltaTime || 0;
            this._pcLevel = parseInt(metadata.pcLevel);
            this._parentalGenres = metadata.pcExtendedRatings.length ? metadata.pcExtendedRatings : [];
            this._parentalWhitelisted = false;
            this._year = metadata.year;
            this._actors = metadata.actors;
            this._directors = metadata.directors;
            this._seasons = metadata.seasons;
            this._rights = '';
            this._imdbRating = metadata.imdbRating || null;

            if (this._pcLevel === 99) {
                this._pcLevel = 0;
            }

            if (this._seriesId) {
                this._relatedContent = new RelatedContent(this);
            }

            if (this.isVOD() || this.isBundles()) {
                imageEndpoint = ApiManager.getImageAPI('vod/');
            } else {
                imageEndpoint = ApiManager.getImageAPI('epg/');
            }

            this._imageEndpoint = imageEndpoint += '{{imageId}}/{{size}}.jpg{{query}}';
        },

        /**
         * Returns PC level.
         *
         * @returns {number} - PC level.
         */
        getPCLevel: function () {
            return this._pcLevel;
        },

        /**
         * Returns parental genres.
         *
         * @returns {Array} - Parental genres.
         */
        getParentalGenres: function () {
            return this._parentalGenres;
        },

        /**
         * Sets program to be parental whitelisted.
         */
        setParentalWhitelisted: function () {
            this._parentalWhitelisted = true;
        },

        /**
         * Gets if program is whitelisted.
         *
         * @returns {boolean} - If is whitelisted.
         */
        getParentalWhitelisted: function () {
            return this._parentalWhitelisted;
        },

        /**
         * Returns the duration.
         *
         * @returns {number} - The duration.
         */
        getDuration: function () {
            return this._duration;
        },

        /**
         * Returns the duration (catchup duration contains +5min at beginning and +15min at the end).
         *
         * @returns {number} - The duration.
         */
        getCatchupDuration: function () {
            return this._catchupDuration;
        },

        /**
         * Returns the duration.
         *
         * @returns {number} - The duration.
         */
        getRecordingDuration: function () {
            return this._recordingDuration;
        },

        /**
         * Returns the start time.
         *
         * @returns {Date} - The start time.
         */
        getStartTime: function () {
            return this._startTime;
        },

        /**
         * Returns the end time.
         *
         * @returns {Date} - The end time.
         */
        getEndTime: function () {
            return this._endTime;
        },

        /**
         * Return id of the broadcast.
         *
         * @returns {string} - The id.
         */
        getId: function () {
            return this._id;
        },

        /**
         * Returns title of the broadcast.
         *
         * @returns {string} - The title.
         */
        getTitle: function () {
            return this._title;
        },

        /**
         * Returns the title brief (short title).
         *
         * @returns {string} - The title brief.
         */
        getTitleBrief: function () {
            return this._titleBrief;
        },

        /**
         * Returns URL of preview image.
         *
         * @param {string} size - Size of the image.
         * @returns {string} - URL of preview image.
         */
        getImageUrl: function (size) {
            var query = '';

            size = size || Math.round(landscapeFormat.width) + 'x' + Math.round(landscapeFormat.height);

            if (this.isLocked()) {
                query += '?blurred=true';
            }

            return Utils.formatTemplate(this._imageEndpoint, {
                imageId: this._imageUrl,
                size: size,
                query: query
            });
        },

        /**
         * Returns the background.
         *
         * @param {string} orientation - Orientation.
         * @param {Object} [dimensions] - The dimensions. Should contain width and height.
         * @returns {string} - The background url.
         * @private
         */
        getImage: function (orientation, dimensions) {
            var format,
                size,
                query = '';

            orientation = orientation || 'landscape'; // Default to landscape image.

            if (orientation === 'manual') {
                size = dimensions.width + 'x' + dimensions.height;
            } else {
                format = imageFormat[orientation];
                size = Math.round(format.width) + 'x' + Math.round(format.height);
            }

            if (this.isLocked()) {
                query += '?blurred=true';
            }

            return Utils.formatTemplate(this._imageEndpoint, {
                imageId: this._imageUrl,
                size: size,
                query: query
            });
        },

        /**
         * Returns channel ID.
         *
         * @returns {number} - ID of channel.
         */
        getChannelId: function () {
            return this._channelId;
        },

        /**
         * Returns the asset id.
         *
         * @returns {number} - The asset id.
         */
        getAssetId: function () {
            return this._assetId;
        },

        /**
         * Returns the channel logo.
         *
         * @returns {string} - The channel logo.
         */
        getChannelLogo: function () {
            var channel = this.getChannelId() && channelManager.getChannelById(this.getChannelId());

            return channel ? channel.getImage('128') : '';
        },

        /**
         * Returns the video ratings.
         *
         * @returns {Array} - The video ratings.
         */
        getVideoRatings: function () {
            return this._videoRatings;
        },

        /**
         * Returns the details action.
         *
         * @returns {string} - The details action url.
         */
        getDetailsAction: function () {
            return this._detailsAction;
        },

        /**
         * Returns the description.
         *
         * @returns {string} - The description.
         */
        getDescription: function () {
            return this._description;
        },

        /**
         * Returns the age rating.
         *
         * @returns {string} - The age rating.
         */
        getAgeRating: function () {
            return this._ageRating;
        },

        /**
         * Returns the genres.
         *
         * @returns {Array} - The genres.
         */
        getGenres: function () {
            return this._genres;
        },

        /**
         * Returns the episode number.
         *
         * @returns {number} - The episode number.
         */
        getEpisodeNumber: function () {
            return this._episodeNumber;
        },

        /**
         * Returns true if the video can be played.
         *
         * @returns {boolean} - True if the video can be played.
         */
        canPlay: function () {
            var assets;

            if (this.isLive()) {

                // Program is currently playing and can be restarted.
                return true;
            } else if (this.isVOD()) {
                return true;
            } else if (this.isReplayItem()) {

                if (this.getContentType() === 'RECORDING') {
                    assets = this.getAssets();

                    if (assets[0] && assets[0].status) {
                        return assets[0].status === 'RecordSuccess';
                    }

                    return false;

                }

                // Program is finished and can be caught up.
                return this.canCatchup();

            }

            // Program is in the future and can't be watched.
            return false;
        },

        /**
         * Returns true if the item is a replay item.
         *
         * @returns {boolean} - True if the item is a replay item.
         */
        isReplayItem: function () {
            var now = app.getDate() / 1000;

            return this.getEndTime() < now;
        },

        /**
         * Returns true if the video is currently playing.
         *
         * @returns {boolean} - True if the video is currently playing.
         */
        isLive: function () {
            var now = app.getDate() / 1000;

            return this.getStartTime() < now && this.getEndTime() > now;
        },

        /**
         * Returns true if the video is type VOD.
         *
         * @returns {boolean} - True if the video is type VOD.
         */
        isVOD: function () {
            return this._contentType === 'VOD';
        },

        /**
         * Returns true if the video is subtype EPISODE.
         *
         * @returns {boolean} - True if the video is subtype EPISODE.
         */
        isVodEpisode: function () {
            return this._contentSubtype === 'EPISODE';
        },

        /**
         * Returns true if the content is type group of bundles.
         *
         * @returns {boolean} - True if the content is type group of bundles.
         */
        isBundles: function () {
            return this._contentType === 'GROUP_OF_BUNDLES';
        },

        /**
         * Returns true if the asset can be live restarted.
         *
         * @returns {boolean} - True if can be live restarted.
         * @private
         */
        _canRestartLive: function () {
            return this._contentOptions.indexOf(CONTENT_OPTIONS.STARTOVER) >= 0;
        },

        /**
         * Returns true if the video can be caught up.
         *
         * @returns {boolean} - True if can be caught up.
         * @private
         */
        canCatchup: function () {

            if (this._contentType === 'RECORDING') {
                return this._assets.length > 0;
            }

            return this._contentOptions.indexOf(CONTENT_OPTIONS.CATCHUP) >= 0;
        },

        /**
         * Returns true if seeking is allowed.
         *
         * @returns {boolean} - True if seeking is allowed.
         */
        canSeek: function () {
            return this.isVOD() ? true : this._contentOptions.indexOf(CONTENT_OPTIONS.TRICKPLAY) >= 0;
        },

        /**
         * Returns true if recording is allowed.
         *
         * @returns {boolean} - True if recording is allowed.
         */
        canRecord: function () {
            return this._contentOptions.indexOf(CONTENT_OPTIONS.RECORDABLE) >= 0;
        },

         /** Returns the program type.
         *
         * @returns {string} - The program type.
         */
        getProgramType: function () {
            return this._programType;
        },

        /**
         * Returns the related content model.
         *
         * @returns {Object} - The related content model.
         */
        getRelatedContent: function () {
            return this._relatedContent;
        },

        /**
         * Returns the array with assets.
         *
         * @returns {Array} - An array with {{assetId: number, assetType: string}[]|*}.
         */
        getAssets: function () {
            return this._assets;
        },

        /**
         * Returns a series id.
         *
         * @returns {number} - The series id.
         */
        getSeriesId: function () {
            return this._seriesId;
        },

        /**
         * Returns the series title.
         *
         * @returns {string} - The series title.
         */
        getSeriesTitle: function () {
            return this._seriesTitle;
        },

        /**
         * Returns the episode's season.
         *
         * @returns {string} - The episode's season.
         */
        getSeason: function () {
            return this._season;
        },

        /**
         * Returns the recording id.
         *
         * @returns {string} - The recording id.
         */
        getRecordingId: function () {
            return this._recordingId;
        },

        /**
         * Returns the start delta time.
         *
         * @returns {number} - The start delta time.
         */
        getStartDeltaTime: function () {
            return this._startDeltaTime;
        },

        /**
         * Returns the content type.
         *
         * @returns {string} - The content type.
         */
        getContentType: function () {
            return this._contentType;
        },

        /**
         * Returns the content subtype.
         *
         * @returns {string} - The content subtype.
         */
        getContentSubtype: function () {
            return this._contentSubtype;
        },

        /**
         * Returns the year.
         *
         * @returns {string} - The year.
         */
        getYear: function () {
            return this._year;
        },

        /**
         * Returns true if the content type is a recording.
         *
         * @returns {boolean} - Is recording.
         */
        isRecording: function () {
            return this._contentType === 'RECORDING';
        },

        /**
         * Returns true if broadcast is in the future.
         *
         * @returns {boolean} - Is future item.
         */
        isFutureItem: function () {
            return this.getStartTime() && new Date(this.getStartTime() * 1000) > app.getDate();
        },

        /**
         * Gets the master asset ID.
         *
         * @param {Array} [assets] - The assets available for the specific channel.
         * @returns {number} - The master asset ID.
         * @private
         */
        _getMasterAssetId: function (assets) {
            var assetId,
                i;

            for (i = 0; i < assets.length; i++) {
                if (assets[i].assetType === 'MASTER') {

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

            return assetId;
        },

        /**
         * Returns the recording starttime.
         *
         * @returns {number|null} - The stream offset.
         */
        getStreamOffset: function () {
            return this._streamOffset;
        },

        /**
         * Returns true if the broadcast is locked.
         *
         * @returns {boolean} - True if the broadcast is locked.
         */
        isLocked: function () {
            var parentalControlParams = SessionManager.getInstance().getUserPCParams(),
                parentalControlOn = SessionManager.getInstance().isParentalOn(),
                parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalControlLevel')),
                parentalControlGenres = Utils.getNested(parentalControlParams, 'parentalGenres'),
                i,
                self = this,
                isGenreProtected = function () {
                    if (parentalControlGenres.length) {
                        for (i = 0; i < parentalControlGenres.length; i++) {
                            if (self.getParentalGenres().indexOf(parentalControlGenres[i]) > -1) {
                                return true;
                            }
                        }
                    }
                    return false;
                },
                isLocked = false,
                VODContentTypes = ['VOD', 'GROUP_OF_BUNDLES', 'BUNDLE'];

            if (VODContentTypes.indexOf(this.getContentType()) >= 0) {
                parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalVODControlLevel'));
            }

            if (!FeatureManager.getInstance().isParentalEnabled() || !parentalControlOn) {
                isLocked = false;
            } else {
                isLocked = !(parentalControlLevel >= this.getPCLevel()) || isGenreProtected();
            }

            return isLocked;
        },

        /**
         * Returns true if the stream requires to be requested with pin.
         *
         * @returns {boolean} - True if stream requires pin.
         */
        streamRequiresPin: function () {
            var parentalControlParams = SessionManager.getInstance().getUserPCParams(),
                parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalControlLevel')),
                parentalControlGenres = Utils.getNested(parentalControlParams, 'parentalGenres'),
                genres = this.getParentalGenres(),
                isGenreProtected,
                VODContentTypes = ['VOD', 'GROUP_OF_BUNDLES', 'BUNDLE'];

            if (VODContentTypes.indexOf(this.getContentType()) >= 0) {
                parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalVODControlLevel'));
            }

            isGenreProtected = function () {
                var i;

                if (parentalControlGenres.length) {
                    for (i = 0; i < parentalControlGenres.length; i++) {
                        if (genres.indexOf(parentalControlGenres[i]) > -1) {
                            return true;
                        }
                    }
                }
                return false;
            };

            return !(parentalControlLevel >= this.getPCLevel()) || isGenreProtected();
        },

        /**
         * Returns true if the item is playable.
         *
         * @returns {boolean} - True if the item is playable.
         */
        isPlayable: function () {
            var now = app.getDate().getTime(),
                endTime = this.getEndTime() * 1000,
                replayBuffer = 15 * 60 * 1000;

            return (now - endTime > replayBuffer);
        },

        /**
         * Returns the formatted duration text.
         *
         * @returns {string} - The formatted duration text.
         */
        getFormattedDuration: function () {
            var hours,
                duration,
                durationText,
                minutes;

            if (!this._formattedDuration) {
                duration = this.getDuration();
                durationText = '';

                hours = Math.floor(duration / 3600);
                duration = duration - hours * 3600;
                minutes = Math.floor(duration / 60);

                if (hours) {
                    durationText += hours + l10n.get('hours') + ' ';
                }

                if (minutes) {
                    durationText += minutes + l10n.get('minutes');
                }

                this._formattedDuration = durationText;
            }

            return this._formattedDuration;
        },

        /**
         * Returns the actors.
         *
         * @returns {Array} - The actors.
         */
        getActors: function () {

            if (this._directors && this._directors.length) {

                return this._actors.slice(0, 4);
            }

            return this._actors.slice(0, 5);
        },

        /**
         * Returns the directors.
         *
         * @returns {Array} - The directors.
         */
        getDirectors: function () {

            if (this._actors && this._actors.length) {

                if (this._actors.length <= 3) {
                    return this._directors.slice(0, 2);
                }

                return this._directors.slice(0, 1);
            }

            return this._directors.slice(0, 5);
        },

        /**
         * Check if the item's channel is found in the channel list.
         * Channels with no right have already been filtered out on live channels request.
         *
         * @returns {boolean} True if has rights to watch (channel not found) false if has no rights to watch.
         */
        hasChannelRights: function () {
            return !!channelManager.getChannelById(this.getChannelId());
        },

        /**
         * Returns the rights of the asset.
         *
         * @returns {string} - The rights type. ie 'watch', 'buy'.
         */
        getRights: function () {
            return this._rights;
        },

        /**
         * Sets the rights obtained from user data for this content.
         *
         * @param {string} rights - The rights for this content.
         */
        setRights: function (rights) {
            this._rights = rights;
        },

        /**
         * Returns the user rating scale of the content. (IMDB).
         *
         * @returns {number|null} - The user rating scale.
         */
        getImdbRating: function () {
            return this._imdbRating;
        }
    });

    return Broadcast;
});