define(['itemShortcuts', 'inputManager', 'connectionManager', 'playbackManager', 'imageLoader', 'layoutManager', 'browser', 'dom', 'loading', 'focusManager', 'serverNotifications', 'events', 'registerElement'], function (itemShortcuts, inputManager, connectionManager, playbackManager, imageLoader, layoutManager, browser, dom, loading, focusManager, serverNotifications, events) {
    'use strict';

    var ItemsContainerPrototype = Object.create(HTMLDivElement.prototype);

    function onClick(e) {
        var itemsContainer = this;
        var target = e.target;
        var multiSelect = itemsContainer.multiSelect;

        if (multiSelect) {
            if (multiSelect.onContainerClick.call(itemsContainer, e) === false) {
                return;
            }
        }

        itemShortcuts.onClick.call(itemsContainer, e);
    }

    function disableEvent(e) {
        e.preventDefault();
        e.stopPropagation();
        return false;
    }

    function onContextMenu(e) {
        var itemsContainer = this;
        var target = e.target;
        var card = dom.parentWithAttribute(target, 'data-id');

        // check for serverId, it won't be present on selectserver
        if (card && card.getAttribute('data-serverid')) {
            inputManager.trigger('menu', {
                sourceElement: card
            });

            e.preventDefault();
            e.stopPropagation();
            return false;
        }
    }

    function getShortcutOptions() {
        return {
            click: false
        };
    }

    ItemsContainerPrototype.enableMultiSelect = function (enabled) {
        var current = this.multiSelect;

        if (!enabled) {
            if (current) {
                current.destroy();
                this.multiSelect = null;
            }
            return;
        }

        if (current) {
            return;
        }

        var self = this;
        require(['multiSelect'], function (MultiSelect) {
            self.multiSelect = new MultiSelect({
                container: self,
                bindOnClick: false
            });
        });
    };

    function onDrop(evt, itemsContainer) {
        var el = evt.item;

        var newIndex = evt.newIndex;
        var itemId = el.getAttribute('data-playlistitemid');
        var playlistId = el.getAttribute('data-playlistid');

        if (!playlistId) {
            var oldIndex = evt.oldIndex;
            el.dispatchEvent(new CustomEvent('itemdrop', {
                detail: {
                    oldIndex: oldIndex,
                    newIndex: newIndex,
                    playlistItemId: itemId
                },
                bubbles: true,
                cancelable: false
            }));
            return;
        }

        var serverId = el.getAttribute('data-serverid');
        var apiClient = connectionManager.getApiClient(serverId);

        loading.show();

        apiClient.ajax({
            url: apiClient.getUrl('Playlists/' + playlistId + '/Items/' + itemId + '/Move/' + newIndex),
            type: 'POST'
        }).then(function () {
            loading.hide();
        }, function () {
            loading.hide();
            itemsContainer.refreshItems();
        });
    }

    ItemsContainerPrototype.enableDragReordering = function (enabled) {
        var current = this.sortable;
        if (!enabled) {
            if (current) {
                current.destroy();
                this.sortable = null;
            }
            return;
        }

        if (current) {
            return;
        }

        var self = this;
        require(['sortable'], function (Sortable) {
            self.sortable = new Sortable(self, {
                draggable: '.listItem',
                handle: '.listViewDragHandle',

                // dragging ended
                onEnd: function (evt) {
                    return onDrop(evt, self);
                }
            });
        });
    };

    function onUserDataChanged(e, apiClient, userData) {

        var itemsContainer = this;

        require(['cardBuilder'], function (cardBuilder) {
            cardBuilder.onUserDataChanged(userData, itemsContainer);
        });

        var eventsToMonitor = getEventsToMonitor(itemsContainer);

        // TODO: Check user data change reason?
        if (eventsToMonitor.indexOf('markfavorite') !== -1) {
            itemsContainer.notifyRefreshNeeded();
        } else if (eventsToMonitor.indexOf('markplayed') !== -1) {
            itemsContainer.notifyRefreshNeeded();
        }
    }

    function getEventsToMonitor(itemsContainer) {
        var monitor = itemsContainer.getAttribute('data-monitor');
        if (monitor) {
            return monitor.split(',');
        }

        return [];
    }

    function onTimerCreated(e, apiClient, data) {

        var itemsContainer = this;

        if (getEventsToMonitor(itemsContainer).indexOf('timers') !== -1) {
            itemsContainer.notifyRefreshNeeded();
            return;
        }

        var programId = data.ProgramId;
        // This could be null, not supported by all tv providers
        var newTimerId = data.Id;

        require(['cardBuilder'], function (cardBuilder) {
            cardBuilder.onTimerCreated(programId, newTimerId, itemsContainer);
        });
    }

    function onSeriesTimerCreated(e, apiClient, data) {
        var itemsContainer = this;
        if (getEventsToMonitor(itemsContainer).indexOf('seriestimers') !== -1) {
            itemsContainer.notifyRefreshNeeded();
            return;
        }
    }

    function onTimerCancelled(e, apiClient, data) {
        var itemsContainer = this;
        if (getEventsToMonitor(itemsContainer).indexOf('timers') !== -1) {
            itemsContainer.notifyRefreshNeeded();
            return;
        }

        require(['cardBuilder'], function (cardBuilder) {
            cardBuilder.onTimerCancelled(data.Id, itemsContainer);
        });
    }

    function onSeriesTimerCancelled(e, apiClient, data) {
        var itemsContainer = this;
        if (getEventsToMonitor(itemsContainer).indexOf('seriestimers') !== -1) {
            itemsContainer.notifyRefreshNeeded();
            return;
        }

        require(['cardBuilder'], function (cardBuilder) {
            cardBuilder.onSeriesTimerCancelled(data.Id, itemsContainer);
        });
    }

    function onLibraryChanged(e, apiClient, data) {
        var itemsContainer = this;

        var eventsToMonitor = getEventsToMonitor(itemsContainer);
        if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) {
            // yes this is an assumption
            return;
        }

        var itemsAdded = data.ItemsAdded || [];
        var itemsRemoved = data.ItemsRemoved || [];
        if (!itemsAdded.length && !itemsRemoved.length) {
            return;
        }

        var parentId = itemsContainer.getAttribute('data-parentid');
        if (parentId) {
            var foldersAddedTo = data.FoldersAddedTo || [];
            var foldersRemovedFrom = data.FoldersRemovedFrom || [];
            var collectionFolders = data.CollectionFolders || [];

            if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) {
                return;
            }
        }

        itemsContainer.notifyRefreshNeeded();
    }

    function onPlaybackStopped(e, stopInfo) {
        var itemsContainer = this;
        var state = stopInfo.state;

        var eventsToMonitor = getEventsToMonitor(itemsContainer);
        if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
            if (eventsToMonitor.indexOf('videoplayback') !== -1) {
                itemsContainer.notifyRefreshNeeded(true);
                return;
            }
        } else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
            if (eventsToMonitor.indexOf('audioplayback') !== -1) {
                itemsContainer.notifyRefreshNeeded(true);
                return;
            }
        }
    }

    function addNotificationEvent(instance, name, handler, owner) {
        var localHandler = handler.bind(instance);
        owner = owner || serverNotifications;
        events.on(owner, name, localHandler);
        instance['event_' + name] = localHandler;
    }

    function removeNotificationEvent(instance, name, owner) {
        var handler = instance['event_' + name];
        if (handler) {
            owner = owner || serverNotifications;
            events.off(owner, name, handler);
            instance['event_' + name] = null;
        }
    }

    ItemsContainerPrototype.createdCallback = function () {
        this.classList.add('itemsContainer');
    };

    ItemsContainerPrototype.attachedCallback = function () {
        this.addEventListener('click', onClick);

        if (browser.touch) {
            this.addEventListener('contextmenu', disableEvent);
        } else {
            if (this.getAttribute('data-contextmenu') !== 'false') {
                this.addEventListener('contextmenu', onContextMenu);
            }
        }

        if (layoutManager.desktop || layoutManager.mobile) {
            if (this.getAttribute('data-multiselect') !== 'false') {
                this.enableMultiSelect(true);
            }
        }

        if (layoutManager.tv) {
            this.classList.add('itemsContainer-tv');
        }

        itemShortcuts.on(this, getShortcutOptions());

        addNotificationEvent(this, 'UserDataChanged', onUserDataChanged);
        addNotificationEvent(this, 'TimerCreated', onTimerCreated);
        addNotificationEvent(this, 'SeriesTimerCreated', onSeriesTimerCreated);
        addNotificationEvent(this, 'TimerCancelled', onTimerCancelled);
        addNotificationEvent(this, 'SeriesTimerCancelled', onSeriesTimerCancelled);
        addNotificationEvent(this, 'LibraryChanged', onLibraryChanged);
        addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager);

        if (this.getAttribute('data-dragreorder') === 'true') {
            this.enableDragReordering(true);
        }
    };

    ItemsContainerPrototype.detachedCallback = function () {
        clearRefreshInterval(this);

        this.enableMultiSelect(false);
        this.enableDragReordering(false);
        this.removeEventListener('click', onClick);
        this.removeEventListener('contextmenu', onContextMenu);
        this.removeEventListener('contextmenu', disableEvent);

        itemShortcuts.off(this, getShortcutOptions());

        removeNotificationEvent(this, 'UserDataChanged');
        removeNotificationEvent(this, 'TimerCreated');
        removeNotificationEvent(this, 'SeriesTimerCreated');
        removeNotificationEvent(this, 'TimerCancelled');
        removeNotificationEvent(this, 'SeriesTimerCancelled');
        removeNotificationEvent(this, 'LibraryChanged');
        removeNotificationEvent(this, 'playbackstop', playbackManager);

        this.fetchData = null;
        this.getItemsHtml = null;
        this.parentContainer = null;
    };

    ItemsContainerPrototype.pause = function () {
        clearRefreshInterval(this, true);
        this.paused = true;
    };

    ItemsContainerPrototype.resume = function (options) {
        this.paused = false;

        var refreshIntervalEndTime = this.refreshIntervalEndTime;
        if (refreshIntervalEndTime) {

            var remainingMs = refreshIntervalEndTime - new Date().getTime();
            if (remainingMs > 0 && !this.needsRefresh) {
                resetRefreshInterval(this, remainingMs);
            } else {
                this.needsRefresh = true;
                this.refreshIntervalEndTime = null;
            }
        }

        if (this.needsRefresh || (options && options.refresh)) {
            return this.refreshItems();
        }

        return Promise.resolve();
    };

    ItemsContainerPrototype.refreshItems = function () {
        if (!this.fetchData) {
            return Promise.resolve();
        }

        if (this.paused) {
            this.needsRefresh = true;
            return Promise.resolve();
        }

        this.needsRefresh = false;

        return this.fetchData().then(onDataFetched.bind(this));
    };

    ItemsContainerPrototype.notifyRefreshNeeded = function (isInForeground) {
        if (this.paused) {
            this.needsRefresh = true;
            return;
        }

        var timeout = this.refreshTimeout;
        if (timeout) {
            clearTimeout(timeout);
        }

        if (isInForeground === true) {
            this.refreshItems();
        } else {
            this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000);
        }
    };

    function clearRefreshInterval(itemsContainer, isPausing) {
        if (itemsContainer.refreshInterval) {
            clearInterval(itemsContainer.refreshInterval);
            itemsContainer.refreshInterval = null;

            if (!isPausing) {
                itemsContainer.refreshIntervalEndTime = null;
            }
        }
    }

    function resetRefreshInterval(itemsContainer, intervalMs) {
        clearRefreshInterval(itemsContainer);

        if (!intervalMs) {
            intervalMs = parseInt(itemsContainer.getAttribute('data-refreshinterval') || '0');
        }

        if (intervalMs) {
            itemsContainer.refreshInterval = setInterval(itemsContainer.notifyRefreshNeeded.bind(itemsContainer), intervalMs);
            itemsContainer.refreshIntervalEndTime = new Date().getTime() + intervalMs;
        }
    }

    function onDataFetched(result) {
        var items = result.Items || result;

        var parentContainer = this.parentContainer;
        if (parentContainer) {
            if (items.length) {
                parentContainer.classList.remove('hide');
            } else {
                parentContainer.classList.add('hide');
            }
        }

        var activeElement = document.activeElement;
        var focusId;
        var hasActiveElement;

        if (this.contains(activeElement)) {
            hasActiveElement = true;
            focusId = activeElement.getAttribute('data-id');
        }

        this.innerHTML = this.getItemsHtml(items);

        imageLoader.lazyChildren(this);

        if (hasActiveElement) {
            setFocus(this, focusId);
        }

        resetRefreshInterval(this);

        if (this.afterRefresh) {
            this.afterRefresh(result);
        }
    }

    function setFocus(itemsContainer, focusId) {
        if (focusId) {
            var newElement = itemsContainer.querySelector('[data-id="' + focusId + '"]');
            if (newElement) {
                try {
                    focusManager.focus(newElement);
                    return;
                } catch (err) {
                    console.error(err);
                }
            }
        }

        focusManager.autoFocus(itemsContainer);
    }

    document.registerElement('emby-itemscontainer', {
        prototype: ItemsContainerPrototype,
        extends: 'div'
    });
});
