﻿/// <reference path="jquery-1.4.1.min-vsdoc.js" />
/// <reference path="jquery.pagination.chirp.js" />
/// <reference path="netqast.functions.chirp.js" />
/// <reference path="firebug-vsdoc.js" />

(function ($) {
    /// <param name="$" type="jQuery"></param>

    var imgStarOn = "/content/images/stars/staron.png";
    var imgStarOff = "/content/images/stars/staroff.png";
    var starClicked = false;

    $.extend({

        //Ratify jQuery Extension
        ratify: function (event) {
            if (event.type == "mouseenter") {
                onMouseEnter($(this));
            }
            else {
                onMouseLeave($(this));
            }

            function onMouseEnter(rateable) {
                if (typeof (rateable.data("rating")) == "undefined") {
                    var netqastNode = rateable.parents("[netqast]:first");
                    var config = netqast.parseConfig(netqastNode.attr("netqast"));
                    rateable.data("rating", new Rating(config));
                }

                buildRatingControl(rateable);
            };

            function onMouseLeave(rateable) {
                if (starClicked) {
                    $(".noRatingText", rateable).remove();
                    $("a img", rateable).remove();
                    $("a", rateable).append($(".ratingInput img"));
                    rateable.addClass("rated");
                }

                $(".ratingInput").remove();
                $("a", rateable).show();

                starClicked = false;
            };

            function onStarHover() {
                var star = $(this);
                var rating = star.attr("alt");

                $(".ratingInput img:lt(" + rating + ")")
                    .attr("src", imgStarOn);
                $(".ratingInput img:gt(" + (rating - 1) + ")")
                    .attr("src", imgStarOff);
            };

            function onStarClick() {
                starClicked = true;
                $(this).closest(".rateable").data("rating").update($(this).attr("alt"));
            };

            function buildRatingControl(rateable) {
                $("a", rateable).hide();
                var rating = rateable.data("rating");
                var imgDiv = $('<div class="ratingInput" />');

                for (var i = 1; i <= 5; i++) {

                    var imgNode = $("<img />");
                    imgNode.hover(onStarHover);
                    imgNode.click(onStarClick);
                    imgNode.attr("alt", i);
                    imgNode.attr("title", getRatingTitleText(i));

                    if (rating.value() > i) {
                        imgNode.attr("src", imgStarOn);
                    }
                    else {
                        imgNode.attr("src", imgStarOff);
                    }

                    imgDiv.append(imgNode);
                }

                rateable.append(imgDiv);
            };

            function getRatingTitleText(index) {
                if (index == 1)
                    return 'Click to rate as "Really disliked it"';
                if (index == 2)
                    return 'Click to rate as "Disliked it"';
                if (index == 3)
                    return 'Click to rate as "Indifferent"';
                if (index == 4)
                    return 'Click to rate as "Liked it"';
                if (index == 5)
                    return 'Click to rate as "Loved it"';
            }

            function Rating(config) {

                this.config = config;
                this.rating = null;

                this.value = function () {
                    if (this.rating == null) {
                        this.rating = 0;
                    }

                    return this.rating;
                };

                this.update = function (rating) {
                    this.rating = rating;
                    $.post(this.config.url + "userrating", "rating=" + this.rating);
                };
            };
        },

        //Hoverify jQuery Extension
        hoverify: function (event) {
            var hoverDelay = 650;  //milliseconds

            if (event.type == "mouseenter") {
                onMouseEnter($(this), event);
            }
            else {
                onMouseLeave($(this));
            }

            function onMouseEnter(hoverable, event) {
                var hoverItem = $(".hoverItem", hoverable);
                hoverable.data("hovered", true);

                setTimeout(function () {
                    if (isHovered(hoverable)) {
                        if (hoverItem.length == 0) {
                            var netqastNode = hoverable.parents("[netqast]:first");
                            var config = netqast.parseConfig(netqastNode.attr("netqast"));

                            //Ajax request for hover item html.
                            $.get(config.url + "hover_html", function (data) {
                                hoverable.append(data);
                                var relativeX = event.pageX - $("#PageWrap").offset().left;
                                if (relativeX > 560)
                                    $(".hoverItem", hoverable).addClass("hoverItemLeft");
                                else
                                    $(".hoverItem", hoverable).addClass("hoverItemRight");

                                if (!isHovered(hoverable)) {
                                    $(".hoverItem", hoverable).hide();
                                }
                            });
                        }
                        else {
                            hoverItem.show();
                        }
                    }
                }, hoverDelay);
            };

            function onMouseLeave(hoverable) {
                hoverable.data("hovered", false);
                $(".hoverItem", hoverable).hide();
            };

            /// <summary>Convenience method for testing if the user is still hovering over the hoverable item.</summary>
            /// <param type="jQuery" name="hoverable">The hoverable item.</param>
            /// <returns type="boolean" />
            function isHovered(hoverable) {
                return hoverable.data("hovered");
            };
        }

    });

    //Browsify jQuery Plugign
    $.fn.browsify = function (settings) {

        settings = $.extend({
            "itemContainerClass": "browseContainer",
            "itemContentClass": "browseContent"
        }, settings);

        var eventHandler = {
            onSortBySelectChange: function () {
                var sortSelect = $(this);
                var paginator = sortSelect.data("paginateContainer").data("paginator");
                paginator
                    .sortBy($("option:selected", sortSelect).val())
                    .renderNavigation()
                    .current();
            }
        };

        browsifyThis = function () {
            var paginateContainer = $(this),
                browseContainer;

            //Look up the controls container div and cache it.
            if (netqast.isUndefined(paginateContainer.data("browseContainer"))) {
                var controlType = "browse";
                browseContainer = paginateContainer.closest("." + settings.itemContainerClass);
                if (browseContainer.length == 0) {
                    controlType = "pagingBrowse";
                    browseContainer = paginateContainer.closest("#PagingBrowseContainer");
                }

                paginateContainer.data("controlType", controlType);
                paginateContainer.data("browseContainer", browseContainer);
            }
            else
                browseContainer = paginateContainer.data("browseContainer");

            var leftArrow, rightArrow, seeAllLink, sortSelect, config = { useLegacyPaginator: true };
            switch (paginateContainer.data("controlType")) {

                case "browse":
                    //Hook up left arrow to the selected paginateContainer
                    leftArrow = browseContainer
                        .find("[id$='_lnkLeftArrow']")
                        .browseArrowLinkify("left", paginateContainer);

                    //Hook up right arrow to the selected paginateContainer
                    rightArrow = browseContainer
                        .find("[id$='_lnkRightArrow']")
                        .browseArrowLinkify("right", paginateContainer);
                    break;

                case "pagingBrowse":
                    config.useLegacyPaginator = false;

                    //Hook up left arrow to the selected paginateContainer
                    leftArrow = browseContainer
                        .find("#lnkLeftArrow")
                        .browseArrowLinkify("left", paginateContainer);

                    //Hook up right arrow to the selected paginateContainer
                    rightArrow = browseContainer
                        .find("#lnkRightArrow")
                        .browseArrowLinkify("right", paginateContainer);

                    browseContainer
                        .find(".pagingBrowseHeader select")
                        .data("paginateContainer", paginateContainer)
                        .change(eventHandler.onSortBySelectChange);

                    browseContainer
                        .find(".footerLinks a")
                        .data("paginateContainer", paginateContainer);

                    break;

                default:
                    alert("Invalid browse control detected in jQuery Browsify plugin");

            };

            //Only need to setup the pagination class once for each paginateContainer.
            if (paginateContainer.data("paginator") == null) {
                var itemContainer = browseContainer.find("." + settings.itemContentClass + ":first"),
                    rawConfig = paginateContainer.attr("netqast");

                $.extend(config, netqast.parseConfig(rawConfig));

                paginateContainer.data("paginator",
                    new Paginator(
                        config,
                        leftArrow,
                        rightArrow,
                        itemContainer,
                        browseContainer
                    )
                );
            }

            //Set the arrows based on the selected genrelink.
            paginateContainer.data("paginator").renderNavigation();
        };

        /**
        * This class manages the pagination of the browse control.
        *
        * @param {object} config {itemType:"song", genreName:"alternative", genreId:2}
        * @param {jQuery} leftArrow Reference to the left arrow.
        * @param {jQuery} rightArrow Reference to the right arrow.
        * @param {jQuery} itemContainer Reference to the item container.
        * @param {jQuery} browseContainer Referernce to the browse container.
        */
        function Paginator(config, leftArrow, rightArrow, itemContainer, browseContainer) {

            this.config = config;
            this.leftArrow = leftArrow;
            this.rightArrow = rightArrow;
            this.itemContainer = itemContainer;
            this.browseContainer = browseContainer;
            this.start = parseInt(config.start, 10);
            this.count = parseInt(config.count, 10);
            this.totalCount = parseInt(config.totalCount, 10);
            this.currentItemCount = null;
            this.currentPage = this.start > 0 ? Math.floor(this.start / this.count) : 0;
            var lastPageNode = browseContainer.find(".footerLinks .lastPage");
            this.lastPage = (lastPageNode != null && lastPageNode.length > 0) ? parseInt(lastPageNode.html(), 10) : null;

            this.sortBy = function (sort) {
                this.config.sortby = sort;
                return this;
            };

            this.current = function () {
                this.load();
                return this;
            };

            this.previous = function () {
                if (this.start >= this.count) {
                    this.start -= this.count;
                    this.currentPage--;
                    this.load(true);
                }

                return this;
            };

            this.next = function () {
                if (this.currentItemCount >= this.count) {
                    this.start += this.count;
                    this.currentPage++;
                    this.load(true);
                }

                return this;
            };

            this.paginate = function (pageNumber, $container) {
                this.currentPage = pageNumber;
                this.start = pageNumber * this.count;
                this.load();

                return this;
            };

            this.paginatedUrl = function () {
                return this.config.url
                    + "?sortby=" + this.config.sortby
                    + "&start=" + this.start
                    + "&count=" + this.count
                    + "&contenttype=" + this.config.contenttype;
            };

            this.renderNavigation = function () {
                //Initialize currentItemCount if this is the first time being loaded.
                if (this.currentItemCount == null) {
                    this.currentItemCount = this.itemContainer.find(".browseListItem").length;
                }

                var url = "/" + this.config.contenttype + "/" + (this.config.genre || "") + "?sortby=" + this.config.sortby + "&count=" + this.count;

                if (!this.config.useLegacyPaginator) {
                    $(".containerFooter", this.browseContainer).empty().pagination(this.totalCount, {
                        items_per_page: this.count,
                        current_page: this.currentPage,
                        next_text: "Next >>",
                        prev_text: "<< Previous",
                        pages_show_always: false,
                        num_display_entries: 5,
                        num_edge_entries: 2,
                        callback: $.proxy(this, "paginate")
                    });

                    return this;
                }

                if (this.start < this.count)
                    this.leftArrow.hide();
                else {
                    this.leftArrow
                        .attr("href", url + "&start=" + (this.start - this.count))
                        .show();
                }

                if (this.currentItemCount < this.count)
                    this.rightArrow.hide();
                else {
                    this.rightArrow
                        .attr("href", url + "&start=" + (this.start + this.count))
                        .show();
                }

                //Updates the "see all" link (just so the hover status displays correctly)
                this.browseContainer
                    .find("[id$='_lnkSeeAll']")
                    .attr("href", "/" + this.config.contenttype + "/" + this.config.genre);

                return this;
            };

            this.load = function (updateNav) {
                $.ajax({
                    url: this.paginatedUrl(),
                    beforeSend: $.proxy(this, "ajaxBeforeSend"),
                    success: $.curry(this, "ajaxSuccess", updateNav),
                    error: $.proxy(this, "ajaxError"),
                    complete: $.proxy(this, "ajaxComplete")
                });
            };

            this.ajaxBeforeSend = function (xhr) {
                //TODO Add code here to handle starting the browse control Ajax loader image.
            };

            this.ajaxComplete = function (xhr, status) {
                //TODO Add code here to handle stopping the browse control Ajax loader image.
            };

            this.ajaxSuccess = function (updateNav, data, status) {
                this.itemContainer.html(data).append($("<div/>").addClass("clear"));
                this.currentItemCount = $(".browseListItem", data).length;
                if (updateNav)
                    this.renderNavigation();
            };

            this.ajaxError = function (xhr, status, exception) {
                //TODO Add Ajax error handling code here if necessary.
            };
        };

        return this.each(browsifyThis);
    };

    //GenreLinkify jQuery Plugin
    $.fn.browseGenreLinkify = function () {
        browseGenreLinkifyThis = function () {
            $(this).click(genreLinkClick);
        };

        function genreLinkClick(event) {
            event.preventDefault();

            var genreLink = $(this);
            var genreListItem = genreLink.parent();
            var genreList = genreListItem.parent();

            genreList
                    .children(".featureGenreLink:first")
                    .removeClass("featureGenreLink")
                    .addClass("genreListItem");

            genreListItem
                    .removeClass("genreListItem")
                    .addClass("featureGenreLink");

            genreLink
                    .browsify()
                    .currentPage();
        }

        return this.each(browseGenreLinkifyThis);
    };

    $.fn.currentPage = function () {
        currentThis = function () {
            var genreLink = $(this);

            if (genreLink.data("paginator") != null) {
                genreLink.data("paginator").current();
            }
            else {
                alert("Current genre link has no associated paginate data.");
            }
        };

        return this.each(currentThis);
    };

    //BrowseArrowLinkify jQuery Plugin
    $.fn.browseArrowLinkify = function (direction, genreLink) {
        browseArrowLinkifyThis = function () {
            var arrow = $(this);

            // Make sure to only register the click event once
            // since this is invoked every time the tab changes.
            if (typeof (arrow.data("clickEventRegistered")) == "undefined") {
                arrow
                    .data("clickEventRegistered", true)
                    .click(arrowClick);
            }

            arrow
                .data("direction", direction)
                .data("genreLink", genreLink);
        };

        function arrowClick(event) {
            event.preventDefault();

            var arrow = $(this);

            if (arrow.data("genreLink") != null) {
                var genreLink = arrow.data("genreLink");
                var direction = arrow.data("direction");

                switch (direction) {
                    case "left":
                        genreLink.data("paginator").previous();
                        break;

                    case "right":
                        genreLink.data("paginator").next();
                        break;

                    default:
                        alert("Invalid arrow direction detected: " + direction);
                }
            }
            else {
                alert("Arrow has no associated genre link.");
            }
        };

        return this.each(browseArrowLinkifyThis);
    };

    $.curry = function (fn, proxy) {
        ///	<summary>
        ///		Just like proxy, but enhanced with the ability to "curry" arguments.
        ///     Takes a function and returns a new one that will always have a particular scope.
        ///	</summary>
        /// <remarks>
        ///     Not replacing the proxy method because there are still some edge cases where this breaks proxy.
        /// </remarks>
        /// <example>
        ///     Any of the following signatures will bind a function to a particular context and return the bound function.
        ///
        /// jQuery.curry( function, scope )
        /// jQuery.curry( scope, name )
        /// jQuery.curry( function, scope, args... )
        /// jQuery.curry( scope, name, args... )
        /// </example>
        ///	<param name="fn" type="Function">
        ///		The function whose scope will be changed.
        ///	</param>
        ///	<param name="proxy" type="Object">
        ///		The object to which the scope of the function should be set.
        ///	</param>
        ///	<returns type="Function" />

        var context, args = Array.prototype.slice.call(arguments, 2);

        if (arguments.length >= 2) {

            if (typeof proxy === "string") {
                context = fn;
                fn = context[proxy];
                proxy = undefined;

            } else if (proxy && !jQuery.isFunction(proxy)) {
                context = proxy;
                proxy = undefined;

            }
        }

        if (!proxy && fn) {
            proxy = function () {
                var combinedArgs = jQuery.merge([], args);
                combinedArgs = jQuery.merge(combinedArgs, arguments);
                return fn.apply(context || this, combinedArgs);
            };
        }

        // Set the guid of unique handler to the same of original handler, so it can be removed
        if (fn) {
            proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
        }

        // So proxy can be declared as an argument
        return proxy;
    };
})(jQuery);
