LFM.set("Library", {
    Pagination: Class.create({
        initialize: function (container, url, pagination, page, perpage, numpages)
        {
            this.container = $(container);
            this.url = url;
            this.currentPage = page || 1;
            if (window.location.hash) {
                var prollyPage = window.location.hash.match(/^#p(\d+)$/i);
                if (prollyPage && prollyPage[1]) this.currentPage = prollyPage[1] * 1;
            }
            this.supposedPage = this.currentPage;
            this.pageNumberDisplays = [];
            this.lastPage = numpages || 999;
            this.perpage = perpage;
            this.pageSpan = 2;
            
            this.queue = [];
            this.sliding = false;
            
            if (Object.isArray(pagination)) {
                this.pagination = pagination;
            } else {
                this.pagination = [pagination];
            }
            
            this.pagination.each(function (pagination) {
                this.setUpPagination(pagination);
            }, this);
            
            this.defaultParams = $(this.pagination[0]).down("a.nextPage").href.toQueryParams();
            
            this.pageNumberDisplays.invoke("update", this.currentPage);
            this.pages = $H();
            if ($("page" + this.currentPage)) {
                this.pages.set(this.currentPage, {
                    loaded: false,
                    loading: false,
                    placeholder: true,
                    node: $("page" + this.currentPage),
                    number: this.currentPage
                });
            } else {
                var currentPageParams = Object.extend(Object.clone(this.defaultParams), {page: this.currentPage});
                this.container.innerHTML = "";
                this.createPage(currentPageParams);
            }
            
            this.displayedPages = {
                left: false,
                centre: this.pages.get(this.currentPage),
                right: false
            };
            
            this.pages.get(this.currentPage).node.setStyle({
                top: "0",
                left: "0",
                position: "relative"
            }).show();
            
            // load the first next page
            var nextPage = this.currentPage + 1;
            if (nextPage <= this.lastPage) {
                var nextParams = Object.extend(Object.clone(this.defaultParams), {page: nextPage});
                this.createPage(nextParams);
                this.displayedPages.right = this.pages.get(this.currentPage + 1);    
                this.displayedPages.right.node.setStyle({
                    top: "0",
                    left: "100%",
                    position: "absolute"
                }).show();
            }
            
            var nextNextPage = this.currentPage + 2;
            if (nextNextPage <= this.lastPage) {
                var nextNextParams = Object.extend(Object.clone(this.defaultParams), {page: nextNextPage});
                this.createPage(nextNextParams);
            }
            
            var previousPage = this.currentPage - 1;
            if (previousPage != 0) {
                var previousParams = Object.extend(Object.clone(this.defaultParams), {page: previousPage});
                this.createPage(previousParams);
                this.displayedPages.left = this.pages.get(this.currentPage - 1);    
                this.displayedPages.left.node.setStyle({
                    top: "0",
                    left: "-100%",
                    position: "absolute"
                
                }).show();
            }
            
            document.observe("keyup", function (event) {
                if (event.keyCode == Event.KEY_LEFT) {
                    this.previousPage(event);
                    
                } else if (event.keyCode == Event.KEY_RIGHT) {
                    this.nextPage(event);
                }
            }.bind(this));
        },
        setUpPagination: function (pagination)
        {
            var pagination = $(pagination);
            pagination.down("a.previousPage").observe("click", this.previousPage.bindAsEventListener(this));
            pagination.down("a.nextPage").observe("click", this.nextPage.bindAsEventListener(this));
            
            var alphabet = pagination.down("ul.alphabet");
            if (alphabet) alphabet.observe("click", this.jumpToPage.bindAsEventListener(this));
            
            var jumpto = pagination.down("form.jumpto");
            if (jumpto) {
                var select = jumpto.down("select");
                if (select) select.observe("change", this.jumpToPage.bindAsEventListener(this));
                jumpto.select("input").invoke("hide");
            }
            
            var sortorder = pagination.down("p.sortorder");
            if (sortorder) sortorder.observe("click", this.switchSortOrder.bindAsEventListener(this));
            
            var pageNumber = pagination.down("span.pagenumber");
            if (pageNumber) this.pageNumberDisplays.push(pageNumber);
        },
        setCurrentPage: function (page)
        {
            this.currentPage = page;
            this.pageNumberDisplays.invoke("update", page);
            window.location.hash = "#p" + page;
        },
        previousPage: function (event)
        {
            event.stop();
            event.element().blur();
            if (this.supposedPage == 0) return;
            var params = Object.clone(this.defaultParams);
            params.page = this.supposedPage - 1 || 1;
            this.queue.push(params);
            this.workQueue();
        },
        nextPage: function (event)
        {
            event.stop();
            event.element().blur();
            if (this.lastPage && this.lastPage == this.supposedPage) return;
            var params = Object.clone(this.defaultParams);
            params.page = this.supposedPage + 1;
            this.queue.push(params);
            this.workQueue();
        },
        purgeCache: function (params)
        {
            var page = params.page;
            var start = page - this.pageSpan || 1;
            var end = page + this.pageSpan;
            end = end > this.lastPage ? this.lastPage : end;
            
            var removed = false;
            this.pages.each(function (pair) {
                if (pair.key < start || pair.key > end) {
                    log("deleting page " + pair.key);
                    removed = this.pages.unset(pair.key);
                    if (removed) removed.node.remove();
                }
            }, this);
        },
        workQueue: function ()
        {
            log("go to page");
            if (!this.queue.length || this.sliding) return;
            
            var params = this.queue.shift();
            
            this.supposedPage = params.page;
            // load the page that is to be shown
            if (!this.pages.get(params.page)) {
                this.createPage(params, true);
            }
            
            // make sure the next page is there in some form
            var fetchPage = params.page > this.currentPage ? params.page + 1 : params.page - 1;
            if (!this.pages.get(fetchPage) && fetchPage > 0 && fetchPage <= this.lastPage) {
                var fetchParams = Object.extend(Object.clone(params), {page: fetchPage});
                this.createPage(fetchParams, true);
            }
            // show the page to be shown
            
            this.slidePage(params.page);
            
            // and prefetch another page
            var fetchMorePage = params.page > this.currentPage ? params.page + 2 : params.page - 2;
            if (!this.pages.get(fetchMorePage) && fetchMorePage > 0 && fetchMorePage <= this.lastPage) {
                var fetchMoreParams = Object.extend(Object.clone(params), {page: fetchMorePage});
                this.createPage(fetchMoreParams, true);
            }
            
            if (!this.pages.get(params.page)) this.loadPage.bind(this).delay(1, params);
            if (fetchParams) this.loadPage.bind(this).delay(1, fetchParams);
            if (fetchMoreParams) this.loadPage.bind(this).delay(1, fetchMoreParams);
            this.purgeCache(params);
        },
        jumpToPage: function (event)
        {
            var element = event.element();
            if (element.match("a")) {
                var params = element.href.toQueryParams();
            } else if (element.match("select")) {
                var params = Object.clone(this.defaultParams);
                params.page = $F(element);
                element.value = "0";
                element.blur();
                if (params.page == "0") return;
            } else {
                return;
            }
            event.stop();
            
            //stop the animations
            var queue = Effect.Queues.get('sliding');
            queue.each(function(effect) { effect.cancel(); });
            // empty the queue
            this.queue = [];
            this.sliding = false;
            
            // figure out where to jump to
            params.page = params.page * 1;
            this.supposedPage = params.page;
            
            // fetching
            var nextPage = params.page + 1;
            if (nextPage <= this.lastPage && !this.pages.get(nextPage)) {
                var nextParams = Object.extend(Object.clone(params), {page: nextPage});
                this.createPage(nextParams);
            }
            var previousPage = params.page - 1;
            if (previousPage != 0 && !this.pages.get(previousPage)) {
                var previousParams = Object.extend(Object.clone(params), {page: previousPage});
                this.createPage(previousParams);
            }
            
            // load the page that is to be shown
            if (!this.pages.get(params.page)) {
                this.createPage(params);
            }
            this.slidePage(params.page);
        },
        loadPage: function (params)
        {
            var params = params;
            var id = params.page;
            if (this.pages.get(id).loading) return;
            this.pages.get(id).loading = true;
            this.pages.get(id).request = new Ajax.Request(this.url, {
                method: "get",
                parameters: Object.extend(params, {ajax: 1}),
                onSuccess: function (response)
                {
                    var ajaxResponse = new LFM.Ajax.Response(response);
                    if(ajaxResponse.isSuccess()) {
                        var page = ajaxResponse.get("page") * 1;
                        this.lastPage = ajaxResponse.get("lastpage") * 1;
                        this.pages.get(id).node.update(ajaxResponse.get("content"));
                        this.pages.get(id).node.id = "page" + page;
                        this.pages.get(id).node.removeClassName("loading");
                        ieHover.add(this.pages.get(id).node);
                        this.pages.get(id).loading = false;
                        this.pages.get(id).placeholder = false;
                        this.pages.get(id).loaded = true;
                        
                        /*
                            Need to keep count of the images, because they all
                            have loading animations that makes paging really really
                            slow in some browsers, so we need to remove them once
                            the images are loaded
                        */
                        this.pages.get(id).images = this.pages.get(id).node.select("img");
                        
                        if (!this.periodicalExecuter) {
                            this.periodicalExecuter = new PeriodicalExecuter(function(pe) {
                                log("periodic execution started");
                                var images = false;
                                this.pages.each(function (pair) {
                                    if (pair.value.images) {
                                        images = pair.value.images;
                                        if (images.length) {
                                            log("length before: " + images.length);
                                            for (var i = images.length - 1; i >= 0; i--) {
                                                if (!images[i]) {
                                                    images[i] = null;
                                                }
                                                if (images[i].complete) {
                                                    var listItem = images[i].up("li");
                                                    if (listItem) listItem.className = "";
                                                    images[i] = null;
                                                }
                                            }
                                            pair.value.images = images.compact();
                                            log("length after: " + pair.value.images.length);
                                        }
                                    }
                                }, this);
                                log("periodic execution ended");
                            }.bind(this), 10);
                            this.periodicalExecuter.execute();
                        }
                        this.pages.get(id).request = null;
                        delete this.pages.get(id).request;
                    } else {
                        this.pages.get(id).request = null;
                        delete this.pages.get(id).request;
                        this.loadPage(params);
                    }
                }.bind(this),
                onFailure: function (response)
                {
                    this.pages.get(id).request = null;
                    delete this.pages.get(id).request;
                    this.loadPage(params);
                }.bind(this)
            });
        },
        slidePage: function (page)
        {
            var page = page;
            log("paging");
            if ((this.displayedPages.right && this.displayedPages.right.number == page) || (this.displayedPages.left && this.displayedPages.left.number == page)) { 
                // it’s a page right next to the current one,
                // so we can just slide it over
                
                if (this.displayedPages.right.number == page) {
                    this.displayedPages.right.node.addClassName("visible");
                    var options = {
                        x: this.container.getWidth() * -1,
                        y: 0,
                        duration: 0.5,
                        queue: {
                            position: 'end',
                            scope: 'sliding'
                        },
                        beforeStart: function ()
                        {
                            this.sliding = true;
                        }.bind(this),
                        afterFinish: function ()
                        {
                            if (this.displayedPages.left) {
                                this.displayedPages.left.node.hide();
                            }
                            this.displayedPages.left = this.displayedPages.centre;
                            this.displayedPages.centre = this.displayedPages.right;
                            if (this.pages.get(page + 1)) {
                                this.displayedPages.right = this.pages.get(page + 1);
                            } else {
                                this.displayedPages.right = false;
                            }
                            this.resetPositioning();
                            this.setCurrentPage(page);
                            this.sliding = false;
                            this.workQueue();
                        }.bind(this)
                    };
                } else if (this.displayedPages.left.number == page) {
                    this.displayedPages.left.node.addClassName("visible");
                    var options = {
                        x: this.container.getWidth(),
                        y: 0,
                        duration: 0.5,
                        queue: {
                            position: 'end',
                            scope: 'sliding'
                        },
                        beforeStart: function ()
                        {
                            this.sliding = true;
                        }.bind(this),
                        afterFinish: function ()
                        {
                            if (this.displayedPages.right) {
                                this.displayedPages.right.node.hide();
                            }
                            this.displayedPages.right = this.displayedPages.centre;
                            this.displayedPages.centre = this.displayedPages.left;
                            if (this.pages.get(page - 1)) {
                                this.displayedPages.left = this.pages.get(page - 1);
                            } else {
                                this.displayedPages.left = false;
                            }
                            this.resetPositioning();
                            this.setCurrentPage(page);
                            this.sliding = false;
                            this.workQueue();
                        }.bind(this)
                    };
                }
                new Effect.Move(this.container, options);
            } else {
                // the page is somewhere else, so we can’t slide it
                // but just show it
                
                if (this.displayedPages.right) {
                    this.displayedPages.right.node.hide();
                    this.displayedPages.right = false;
                }
                if (this.pages.get(page + 1)) {
                    this.displayedPages.right = this.pages.get(page + 1);
                    this.displayedPages.right.node.show();
                }
                this.displayedPages.centre.node.hide();
                this.displayedPages.centre = this.pages.get(page);
                this.displayedPages.centre.node.show();
                if (this.displayedPages.left) {
                    this.displayedPages.left.node.hide();
                    this.displayedPages.left = false;
                }
                if (this.pages.get(page - 1)) {
                    this.displayedPages.left = this.pages.get(page - 1);
                    this.displayedPages.left.node.show();
                }
                this.resetPositioning();
                this.setCurrentPage(page);
                log("last page: " + this.lastPage);
                log("supposed page: " + this.supposedPage);
                log("current page: " + this.currentPage);
            }
        },
        resetPositioning: function ()
        {
            this.displayedPages.centre.node.setStyle({
                left: "0",
                position: "relative"
            }).addClassName("visible").show();
            if (this.displayedPages.left) {
                this.displayedPages.left.node.setStyle({
                    left: "-100%",
                    position: "absolute"
                }).removeClassName("visible").show();
            }
            if (this.displayedPages.right) {
                this.displayedPages.right.node.setStyle({
                    left: "100%",
                    position: "absolute"
                }).removeClassName("visible").show();
            }
            this.container.setStyle({
                left: "0"
            });
        },
        createPage: function (params, donotload)
        {
            this.pages.set(params.page, {
                loaded: false,
                loading: false,
                placeholder: true,
                node: this.makePlaceholderPage(params),
                number: params.page
            });
            if (!donotload) this.loadPage(params);
        },
        makePlaceholderPage: function (params)
        {
            var id = "page" + params.page;
            var div = new Element("div", {id: id, className: "loading page"});
            div.insert('<ul class="libraryItems artistsLarge">' + '<li class="loading"><span class="pictureFrame"><span class="image"></span><span class="overlay"></span></span></li>'.times(this.perpage) + '</ul>');
            div.hide();
            this.container.insert(div);
            return div;
        },
        /*
            Hook for the deleter. When something is deleted, we need to fill
            up that space with the next artist
        */
        deletion: function ()
        {
            // only needs to do this if not on last page
            for (var i = this.currentPage; i < this.lastPage; i++) {
                log("doing the deletion stuff thing, " + i);
                /*
                    loops through all the pages from the current one on,
                    putting the first child of the next page into the current one
                    until there is no next page (in that case, reloads the page)
                */
                if (this.pages.get(i)) {
                    if (!this.pages.get(i + 1)) {
                        log("no next page, thus loading the page anew");
                        var params = Object.extend(Object.clone(this.defaultParams), {page: i});
                        this.loadPage.bind(this)(params);
                    } else {
                        log("getting first child from next page");
                        var child = this.pages.get(i+1).node.down("li");
                        this.pages.get(i).node.down("ul").appendChild(child);
                    }
                }
            };
        },
        switchSortOrder: function (event)
        {
            var link = event.element();
            if (!link.match("a")) return;
            var params = link.href.toQueryParams();
            if (this.defaultParams.sortOrder == params.sortOrder) return;
            event.stop();
            this.defaultParams = params;
            this.supposedPage = 1;
            var containerHeight = this.container.getHeight();
            this.container.setStyle({
                height: containerHeight + "px"
            });
            this.container.innerHTML = "";
            this.pages = $H();
            this.createPage(params);
            var fetchParams = Object.extend(Object.clone(params), {page: 2});
            this.createPage(fetchParams);
            this.displayedPages = {};
            this.displayedPages.left = false;
            this.displayedPages.centre = this.pages.get("1");
            this.displayedPages.right = this.pages.get("2");
            this.resetPositioning();
            this.setCurrentPage(1);
            link.siblings().invoke("removeClassName", "current");
            link.addClassName("current");
            this.container.setStyle({
                height: "auto"
            });
        }
    }),
    DeleteListener: Class.create({
        initialize: function (container)
        {
            this.container = $(container) || document;
            this.container.observe("click", this.bin.bindAsEventListener(this));
        },
        onDone: function (response) {
            // when deleting items, the links in the A–Z strip
            // at the top of the library need to
            // point to new pages
            var newAlphabet = response.get('alphabet');
            $$('.alphabet').each(function(alphabetContainer) {
                alphabetContainer.select('li a').each(function(letterLink) {
                    var letter = letterLink.innerHTML.strip();
                    if(newAlphabet[letter]) {
                        var href = letterLink.getAttribute('href');
                        var parts = $H(href.toQueryParams());
                        parts.set('page', newAlphabet[letter]);
                        href = href.split("?").shift() + '?' + parts.toQueryString();
                        letterLink.setAttribute('href', href);
                    } else {
                        var li = letterLink.up('li');
                        letterLink.remove();
                        li.appendChild(document.createTextNode(letter));
                    }
                });
            });
            // and if there’s one of those ‘this letter starts here’
            // indicators it needs to be moved
            var item = response.getResourceElement();
            if (item.match("li")) {
                var letter = item.down("strong.letter");
                if (letter) {
                    var next = item.next();
                    if(next && !next.down('strong.letter')) {
                        next.insert({top: letter});
                    }
                }
                item.remove();
                if (LFM.Page && LFM.Page.LibraryPagination) {
                    LFM.Page.LibraryPagination.deletion.bind(LFM.Page.LibraryPagination)();
                }
            } else if (item.match("tr")) {
                var table = item.up("table");
                item.remove();
                if (table.hasClassName("candyStriped")) { 
                    LFM.Display.candyStripe(table.down("tbody"));
                }
            }
        },
        bin: function (event)
        {
            var link = event.findElement("a.remove");
            if (!link) link = event.findElement("form.dismiss");
            if (!link) return;
            event.stop();
             
            var item = link.up("li");
            if (!item) item = link.up("tr");
            var res = item.getResource();
            
            LFM.RemoveFromLibrary(res, {
                fadeDone: true,
                onDone: this.onDone
            });
        }
    }),
    setupArtistTagger: function() {
        $('addTags').observe('click', function (event) {
            event.stop();
            LFM.Omniture.prepareEvar('ClickSource', 'libraryTagBtn');
            LFM.Tag(LFM.Page.resource, {
                onDone: function (response) {
                    var taglist = response.get("tags");
                    var container = $("theTags");
                    var addlink = $("addTags");
                    
                    if (taglist.size) {
                        var tagTemplate = new Template('<a href="' + LFM.Session.libraryURL + '/tags?tag=#{url}">#{name}</a>');

                        container.update(taglist.items.collect(function (tag) {
                            return tagTemplate.evaluate({name: tag.res.name, url: encodeURIComponent(tag.res.name)});
                        }).join(", "));
                        addlink.update(LFM.Page.textWithTags);
                        $("itemTags").down("p.allTags").insert({top: addlink});
                    } else {
                        container.update("");
                        addlink.update(LFM.Page.textWithoutTags);
                        container.up().down("img").insert({after: addlink});
                    }
                    new Effect.Highlight('itemTags');
                }
            });
        });
    },
    RemoveTagFromItemListener: Class.create({
        initialize: function (container) {
            this.container = $(container) || document;
            this.container.observe("click", this.removeTagFromItem.bindAsEventListener(this));
        },
        onSuccess: function (response) {
            if (response.isSuccess()) {
                new Effect.Fade(response.getResourceElement(), {
                    duration: 0.3,
                    afterFinish: function (object) {
                        object.element.remove();
                    }
                });
            }
        },
        removeTagFromItem: function (event) {
            form = event.findElement("form.dismiss");
            if (!form) {
                return;
            }
            event.stop();
            
            var that = this;
            form.request({
                onSuccess: function (transport) {
                    var response = new LFM.Ajax.Response(transport);
                    that.onSuccess(response);
                }
            });
        }
    })
});
