Source: managers/recording.js

define('application/managers/recording', [
    'rofl/class',
    'rofl/lib/promise',
    'rofl/lib/utils',
    'application/managers/api',
    'antie/runtimecontext',
    'application/events/recordevent'
], function (
    Class,
    Promise,
    Utils,
    ApiManager,
    RuntimeContext,
    RecordEvent
) {
    'use strict';

    var application,
        api,
        instance,
        RecordingManager;

    RecordingManager = Class.extend({

        /**
         * Initialises the recording manager.
         */
        init: function () {
            api = ApiManager.getKPNAPI();
            application = RuntimeContext.getCurrentApplication();
            this._slots = {};
        },

        /**
         * Returns the recordings.
         *
         * @param {Object} options - The options.
         * @returns {Promise} - Promise resolving with the recordings.
         */
        getRecordings: function (options) {
            var promises = [];

            promises.push(this._getEpisodes(options));
            promises.push(this._getSeries(options));

            return Promise.all(promises);
        },

        /**
         * Returns the future recordings.
         *
         * @param {Object} [opts] - The retrieval options.
         * @returns {Promise} - Promise resolving with the future recordings.
         */
        getFutureRecordings: function (opts) {
            var now = application.getDate();

            return this._getEpisodes(opts)
                .then(function (result) {
                    return Utils.filter(result, function (a) {
                        return a.getStartTime() * 1000 > now.getTime();
                    });
                });
        },

        /**
         * Returns the current recordings.
         *
         * @param {Object} [opts] - The retrieval options.
         * @returns {Promise} - Promise resolving with the current recordings.
         */
        getCurrentRecordings: function (opts) {
            var now = application.getDate();

            return this._getEpisodes(opts)
                .then(function (result) {
                    return Utils.filter(result, function (a) {
                        return a.getStartTime() * 1000 < now.getTime();
                    });
                });
        },

        /**
         * Returns the current recordings formatted by series.
         *
         * @param {Object} [opts] - The retrieval options.
         * @returns {Promise} - Promise resolving with the current recordings.
         */
        getCurrentSeriesRecordings: function (opts) {
              var self = this,
                  now = application.getDate();

            return this._getEpisodes(opts)
                .then(function () {
                    var result = self._slots['episodes'].series,
                        formatted = [];

                    Utils.each(result, function (item) {

                        if (item.getAllItems().length) {
                            if (item.getAllItems()[0].getSeriesId()) {
                                formatted.push(item);
                            } else {
                                formatted = formatted.concat(item.getAllItems());
                            }
                        }
                    });

                    formatted = Utils.filter(formatted, function (a) {

                        if (Utils.isFunction(a.getAllItems)) {

                            a.setFormattedItems(Utils.filter(a.getAllItems(), function (b) {
                                return b.getStartTime() * 1000 < now.getTime();
                            }));
                            return a.getItemCount() > 0;
                        }

                        return a.getStartTime() * 1000 < now.getTime();
                    });

                    formatted.sort(function (a, b) {
                        var itemA,
                            itemB;

                        if (Utils.isFunction(a.getItems)) {
                            itemA = a.getItems()[0];
                        } else {
                            itemA = a;
                        }

                        if (Utils.isFunction(b.getItems)) {
                            itemB = b.getItems()[0];
                        } else {
                            itemB = b;
                        }


                        return itemA.getStartTime() > itemB.getStartTime() ? -1 : 1;
                    });

                    return formatted;
                });
        },

        /**
         * Returns the future recordings formatted by series.
         *
         * @param {Object} [opts] - The retrieval options.
         * @returns {Promise} - Promise resolving with the future recordings.
         */
        getPlannedSeriesRecordings: function (opts) {
            var self = this,
                now = application.getDate();

            return this._getEpisodes(opts)
                .then(function () {
                    var result = self._slots['episodes'].series,
                        formatted = [];

                    Utils.each(result, function (item) {

                        if (item.getAllItems().length) {
                            if (item.getAllItems()[0].getSeriesId()) {
                                formatted.push(item);
                            } else {
                                formatted = formatted.concat(item.getAllItems());
                            }
                        }
                    });

                    formatted = Utils.filter(formatted, function (a) {

                        if (Utils.isFunction(a.getAllItems)) {

                            a.setFormattedItems(Utils.filter(a.getAllItems(), function (b) {
                                return b.getStartTime() * 1000 > now.getTime();
                            }));

                            return a.getItemCount() > 0;
                        }

                        return a.getStartTime() * 1000 > now.getTime();
                    });

                    formatted.sort(function (a, b) {
                        var itemA,
                            itemB;

                        if (Utils.isFunction(a.getItems)) {
                            itemA = a.getItems()[a.getItems().length - 1];
                        } else {
                            itemA = a;
                        }

                        if (Utils.isFunction(b.getItems)) {
                            itemB = b.getItems()[b.getItems().length - 1];
                        } else {
                            itemB = b;
                        }


                        return itemA.getStartTime() > itemB.getStartTime() ? 1 : -1;
                    });

                    return formatted;
                });
        },

        /**
         * Adds a recording.
         *
         * @param {number} id - The episode id.
         * @returns {Promise} - Promise resolving with an added recording.
         */
        addEpisodeRecording: function (id) {
            var self = this,
                item,
                slot;

            return api.create('recordings/recording', {
                params: {
                    type: 'episode'
                },
                data: {
                    externalContentId: id,
                    isAutoDeletionEnabled: true
                },
                withCredentials: true
            })
            .then(function (result) {
                slot = self._slots['episodes'];
                slot.ids = slot.ids.concat(result.ids);
                item = result.items[result.ids[0]];
                slot.items[result.ids[0]] = item;

                if (isNaN(item.getSeriesId())) {
                    slot.series['NaN']._items.push(item);
                } else if (!slot.series[item.getSeriesId()]) {
                    slot.series[item.getSeriesId()] = result.series[item.getSeriesId()];
                } else {
                    slot.series[item.getSeriesId()]._items.push(item);
                }

                application.broadcastEvent(new RecordEvent('episode', id));
            });
        },

        /**
         * Adds a recording.
         *
         * @param {number|string} id - The series id.
         * @param {number|string} channelId - The channel id.
         * @returns {Promise} - Promise resolving with an added recording.
         */
        addSeriesRecording: function (id, channelId) {
            var self = this,
                seriesItems;

            return api.create('recordings/recording', {
                params: {
                    type: 'series'
                },
                data: {
                    channelId: channelId,
                    seriesId: parseInt(id),
                    isAutoDeletionEnabled: true,
                    episodeScope: 'ALL',
                    isChannelBoundEnabled: true
                },
                withCredentials: true
            })
            .then(function (result) {
                seriesItems = self._slots['series'].items;
                self._slots['series'] = Utils.extend(self._slots['series'], result, true);

                debugger;
                // Add the result items.
                if (seriesItems) {
                    Utils.each(result.items, function (item) {
                        seriesItems.push(item);
                    });
                }

                application.broadcastEvent(new RecordEvent('series', id));
            });
        },

        /**
         * Deletes an item recording.
         *
         * @param {Object} item - The item to remove from recordings.
         * @returns {Promise} - Promise resolving after deletion.
         */
        deleteEpisodeRecording: function (item) {
            var self = this,
                recordingItem,
                id;

            if (item.getRecordingId && item.getRecordingId()) {
                id = item.getRecordingId();
                recordingItem = item;
            } else {
                recordingItem = this._slots['episodes'].items[parseInt(item.getId())];
                id = recordingItem.getRecordingId();
            }

            return api.destroy('recordings/recording', {
                params: {
                    type: 'episode'
                },
                data: [{
                    recordId: parseInt(id),
                    startDeltaTime: recordingItem.getStartDeltaTime() || 0
                }],
                withCredentials: true
            })
            .then(function () {
                self._removeEpisodeFromStorage(item);

                application.broadcastEvent(new RecordEvent('episode', item.getId(), true));
            });
        },

        /**
         * Deletes a series recording.
         *
         * @param {string} id - The series id.
         * @returns {Promise} - Promise resolving with the series id.
         */
        deleteSeriesRecording: function (id) {
            var self = this,
                series = this._slots['series'].items,
                seriesItem,
                i,
                j;

            for (i = 0, j = series.length; i < j; i++) {
                if (series[i].metadata.seriesId === id || series[i].metadata.liveSeriesId === id) {
                    seriesItem = series[i];
                }
            }

            debugger;

            return api.destroy('recordings/recording', {
                params: {
                    type: 'series'
                },
                data: {
                    seriesIds: [parseInt(seriesItem.metadata.seriesId)],
                    isKeepRecordingsEnabled: true
                },
                withCredentials: true
            })
            .then(function (response) {
                var deleteRecord = response.resultObj.deletedSeriesRecordings[0];

                self._slots['series'].items.splice(series.indexOf(seriesItem), 1);

                self._removeItemsFromSeriesRecording(
                    deleteRecord.recordIds,
                    deleteRecord.seriesId
                    );

                application.broadcastEvent(new RecordEvent('series', id, true));
            });
        },

        /**
         * Retrieves the episodes.
         *
         * @param {Object} [options] - The retrieval options.
         * @returns {Promise} - Promise resolving with the recorded episodes.
         * @private
         */
        _getEpisodes: function (options) {
            options = options || {};

            if (!this._slots['episodes'] || options.forceReload) {
                return api.read('recordings/episodes', {
                    withCredentials: true
                })
                .then(Utils.bind(function (result) {
                    this._slots['episodes'] = result;

                    return result.items;
                }, this))
                ['catch'](Utils.bind(function () {
                    this._slots['episodes'] = {
                        items: []
                    };

                    return [];
                }, this));
            }

            return Promise.resolve(this._slots['episodes'].items);
        },

        /**
         * Returns the recorded series.
         *
         * @param {Object} [options] - The retrieval options.
         * @returns {Promise} - Promise resolving with the recorded series.
         * @private
         */
        _getSeries: function (options) {
            options = options || {};
            if (!this._slots['series'] || options.forceReload) {
                return api.read('recordings/series', {
                    withCredentials: true
                })
                    .then(Utils.bind(function (result) {
                        this._slots['series'] = result;

                        debugger;

                        return result.items;
                    }, this))
                    ['catch'](Utils.bind(function () {
                        this._slots['series'] = {
                            items: []
                        };

                        return [];
                    }, this));
            }

            return Promise.resolve(this._slots['series'].items);
        },

        /**
         * Returns true if there is a recording for the given item.
         *
         * @param {number|string} id - The episodes id.
         * @returns {boolean} - True if the episode is being recorded.
         */
        hasEpisodeRecording: function (id) {
            var episodes = Utils.getNested(this._slots, 'episodes', 'ids');

            if (!episodes) {
                return false;
            }

            return episodes.indexOf(parseInt(id)) >= 0;
        },

        /**
         * Returns true if there is a recording for the given item.
         *
         * @param {number|string} id - The series id.
         * @returns {boolean} - True if the series has a recording.
         */
        hasSeriesRecording: function (id) {
            var series,
                i,
                j;

            if (!id) {
                return false;
            }

            series = Utils.getNested(this._slots, 'series', 'items');
            id = parseInt(id);

            if (!series || !series.length) {
                return false;
            }

            for (i = 0, j = series.length; i < j; i++) {
                if (series[i].metadata.seriesId === id || series[i].metadata.liveSeriesId === id) {
                    return true;
                }
            }

            return false;
        },

        /**
         * Returns true if there is a recording for the given item.
         *
         * @param {number|string} id - The episodes id.
         * @param {number|String} seriesId - The series id.
         * @returns {Promise} - Promise resolving with recording status.
         */
        hasRecording: function (id, seriesId) {
            var hasEpisode = this.hasEpisodeRecording(id),
                hasSeries = this.hasSeriesRecording(seriesId);

            return {
                episode: hasEpisode,
                series: hasSeries
            };
        },

        /**
         * Removes the item from episode storage.
         *
         * @param {Object} item - The item.
         * @private
         */
        _removeEpisodeFromStorage: function (item) {
            var index = this._slots['episodes'].ids.indexOf(parseInt(item.getId()));

            if (index >= 0) {
                this._slots['episodes'].ids.splice(index, 1);
            }

            this._removeItemFromSeries(item);
            delete this._slots['episodes'].items[item.getId()];
        },

        /**
         * Removes the item from the series storage.
         *
         * @param {Object} item - The item to be removed.
         * @private
         */
        _removeItemFromSeries: function (item) {
            var series = this._slots['episodes'].series,
                seriesItem = series[item.getSeriesId()];

            if (!seriesItem) {
                seriesItem = series.NaN; // Yuck.
            }

            item = this._slots['episodes'].items[parseInt(item.getId())];

            if (seriesItem && seriesItem.getItems) {
                seriesItem._items.splice(seriesItem._items.indexOf(item), 1);
            } else {
                seriesItem.splice(seriesItem.indexOf(item), 1);
            }
        },

        /**
         * Removes the items from series recordings.
         *
         * @param {Array} ids - The item recordings to remove.
         * @param {number} seriesId - The series id the recordings belong to.
         * @private
         */
        _removeItemsFromSeriesRecording: function (ids, seriesId) {
            var seriesItem = this._slots['episodes'].series[seriesId],
                self = this;

            if (!seriesItem) {
                return;
            }

            // Collect all the indexes of the items to be removed.
            Utils.each(seriesItem.getItems(), function (item) {

                if (ids.indexOf(item.getRecordingId()) >= 0) {

                    self._removeEpisodeFromStorage(item);
                    application.broadcastEvent(new RecordEvent('episode', item.getId(), true));
                }
            });
        },

        /**
         * Resets the recording manager.
         */
        reset: function () {
            this._slots = {};
        }
    });

    return {

        /**
         * Returns the instance.
         *
         * @returns {RecordingManager} - The recording manager.
         */
        getInstance: function () {
            if (!instance) {
                instance = new RecordingManager();
            }

            return instance;
        }
    };
});