Source: managers/epg.js

define('application/managers/epg', [
    'product-layer/epg/manager',
    'application/managers/api',
    'rofl/lib/utils',
    'rofl/lib/promise',
    'antie/runtimecontext'
], function (
    AbstractEpgManager,
    ApiManager,
    Utils,
    Promise,
    RuntimeContext
) {
    'use strict';

    var api,
        DATA_CHUNK_TIME_SPAN = 24 * 60 * 60,
        instance,
        app,
        EpgManager;

    EpgManager = AbstractEpgManager.extend({

        /**
         * Initialises the EPG manager.
         */
        init: function init () {
            init.base.call(this);

            api = ApiManager.getKPNAPI();
            app = RuntimeContext.getCurrentApplication();

            this.setDataChunkTimeSpan(24 * 60 * 60);
        },

        /**
         * Loads the data for the given channel ids and time.
         *
         * @param {Array} channelIds - The channel ids.
         * @param {number} time - The time in seconds.
         * @returns {Promise} - Promise resolving with epg data.
         */
        load: function (channelIds, time) {

            return api.read('epg', {
                params: {
                    channels: channelIds,
                    startTime: time * 1000,
                    endTime: (time + this.getDataChunkTimeSpan()) * 1000
                }
            });
        },

        /**
         * Initiator of the EPG data loading.
         *
         * @param {Array} channelIds - Channel ID's to fetch.
         * @param {number} time - Regulated time for fetching the correct time slot.
         * @returns {Promise} The load promise.
         */
        loadData: function (channelIds, time) {
            var timeSlot = this._regulateTimeSlot(time),
                self = this,
                slot = {};

            return this.load(channelIds, timeSlot).then(function (epgData) {

                if (epgData) {
                    slot.timeSlot = timeSlot;
                    slot.previousSlot = timeSlot - DATA_CHUNK_TIME_SPAN;
                    slot.nextSlot = timeSlot + DATA_CHUNK_TIME_SPAN;
                    slot.compared = false;

                    // Make sure the time slot exists
                    if (!self._slots[timeSlot]) {
                        self._slots[timeSlot] = {};
                    }

                    if (Utils.keys(epgData).length) {
                        Utils.extend(self._slots[timeSlot], epgData);
                    }

                    return Promise.resolve(epgData);
                }
                return Promise.resolve();
            });
        },

        /**
         * Get the current and prev EPG item for a channel ID.
         *
         * @param {String|Number|Array} channelIds - Channel ID's to load the data for.
         * @returns {Promise} EPG data promise.
         */
        getNowPrev: function (channelIds) {
            var self = this,
                currentTime = Math.round((app.getDate()).getTime() / 1000),
                channels;

            if (Utils.isArray(channelIds)) {
                channels = channelIds;
            } else if (Utils.isString(channelIds) || Utils.isNumber(channelIds)) {
                channels = [channelIds];
            }

            return this.getItem(channels, currentTime).then(function (items) {
                var fillMissingPrevs = Utils.bind(self._fillMissingPrevs, self),
                    fillMissingPrevsInCurrentTime = Utils.bind(self._fillMissingPrevsInCurrentTime, self),
                    prevTime = currentTime - DATA_CHUNK_TIME_SPAN,
                    prevTimeSlot = prevTime - (prevTime % DATA_CHUNK_TIME_SPAN),
                    currentTimeSlot = currentTime - (currentTime % DATA_CHUNK_TIME_SPAN);

                if (currentTime > currentTimeSlot) {
                    return fillMissingPrevsInCurrentTime(currentTimeSlot, items).then(function (filledItems) {
                        var epgItems = {};

                        Utils.each(filledItems, function (value, key) {
                            epgItems[key] = {
                                now: value.item,
                                prev: value.prev
                            };
                        });

                        return fillMissingPrevs(prevTimeSlot, items)
                            .then(function (prevItems) {

                                Utils.each(prevItems, function (value, key) {
                                    epgItems[key] = {
                                        now: value.item,
                                        prev: value.prev
                                    };
                                });
                                return Promise.resolve(epgItems);
                            });
                    });
                }

                return fillMissingPrevs(prevTimeSlot, items).then(function (filledItems) {
                    var epgItems = {};

                    Utils.each(filledItems, function (value, key) {
                        epgItems[key] = {
                            now: value.item,
                            prev: value.prev
                        };
                    });
                    return Promise.resolve(epgItems);
                });
            });
        },

        /**
         * Tries to fill in the blanks, retrieved from the prev time span.
         *
         * @param {number} time - The time to check for.
         * @param {Object} items - The original item to check against.
         * @returns {Promise} Load promise.
         */
        _fillMissingPrevs: function (time, items) {
            var missingPrevs = [],
                self = this;

            Utils.each(items, function (value, key) {
                if (!value.prev) {
                    missingPrevs.push(key);
                }
            });

            return this._loadMissingData(missingPrevs, time).then(function () {
                var i = 0,
                    max = missingPrevs.length,
                    channelId,
                    channelSlot,
                    item,
                    j,
                    itemAmount;

                for (; i < max; i++) {

                    channelId = missingPrevs[i];
                    channelSlot = self._getChannelSlot(channelId, time);

                    for (j = 0, itemAmount = channelSlot.length; j < itemAmount; j++) {

                        item = channelSlot[itemAmount - 1];

                        items[channelId].prev = item || null;
                    }

                }
                return Promise.resolve(items);
            });
        },

        /**
         * Tries to fill in the blanks, retrieved from the prev time span.
         *
         * @param {number} time - The time to check for.
         * @param {Object} items - The original item to check against.
         * @returns {Promise} Load promise.
         */
        _fillMissingPrevsInCurrentTime: function (time, items) {
            var missingPrevs = [],
                self = this;

            Utils.each(items, function (value, key) {
                if (!value.prev) {
                    missingPrevs.push(key);
                }
            });

            return this._loadMissingData(missingPrevs, time).then(function () {
                var i = 0,
                    max = missingPrevs.length,
                    channelId,
                    channelSlot,
                    currentItem,
                    item,
                    j,
                    itemAmount;

                for (; i < max; i++) {

                    channelId = missingPrevs[i];
                    channelSlot = self._getChannelSlot(channelId, time);
                    currentItem = items[channelId].item;

                    for (j = 0, itemAmount = channelSlot.length; j < itemAmount; j++) {

                        if (channelSlot[j] === currentItem) {
                            item = channelSlot[j - 1];
                            items[channelId].prev = item || null;
                        }
                    }
                }

                return Promise.resolve(items);
            });
        }

    });

    return {

        /**
         * Returns an instance of the epg manager.
         *
         * @returns {Object} - The epg manager instance.
         */
        getInstance: function () {
            if (!instance) {
                instance = new EpgManager();
            }

            return instance;
        }
    };
});