// ------------------------------------
// ---------- jquery.timetable --------
// ------------------------------------
// Controls the timetable table loading
// ------------------------------------

; (function ($, window, document, undefined) {

    var pluginName = "timetable";

    function timetable(element, options) {
        this.element = element;
        this._name = pluginName;
        this._defaults = $.fn.timetable.defaults;
        this.options = $.extend({}, this._defaults, options);

        this.init();
    }

    function isValidDate(date) {
        return (date.getMonth && typeof date.getMonth === "function");
    };

    function getDayName(day) {
        var dayNames = ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"];

        return dayNames[day];
    }

    function formatHtmlDateString(dt, longDate) {

        var monthNames = ["January", "February", "March", "April", "May", "June",
          "July", "August", "September", "October", "November", "December"
        ];

        var day = dt.getDay();
        var dayName = getDayName(day);
        var hiddenDayPart = getHidenDayNamePart(day);
        var dd = dt.getDate();
        var daySuffix = getSuffix(dd);
        var mm = monthNames[dt.getMonth()];
        var yyyy = dt.getFullYear();

        if (longDate === false) {
            dayName = "";
            mm = mm.substring(0, 3);
        } else {
            dayName = dayName + hiddenDayPart + " ";
        }

        return dayName +
            dd +
            "<sup>" +
            daySuffix +
            "</sup> " +
            mm +
            " " +
            yyyy;

        return dayName +
            '<span class="visuallyhidden">' +
            hiddenDayPart +
            "</span>" +
            " " +
            dd +
            "<sup>" +
            daySuffix +
            "</sup> " +
            mm +
            " " +
            yyyy;
    };

    function formatIsoDateString(dt) {

        var dd = dt.getDate();
        var mm = dt.getMonth() + 1;
        var yyyy = dt.getFullYear();

        if (dd < 10) {
            dd = "0" + dd;
        }

        if (mm < 10) {
            mm = "0" + mm;
        }

        return "" + yyyy + "-" + mm + "-" + dd;
    };

    function getHidenDayNamePart(day) {
        switch (day) {
            case 3:
                return "nesday";
            case 6:
                return "urday";
            default:
                return "day";
        }
    };

    function getStartOfWeek(dt) {

        var day = dt.getDay();
        var diff = 0;

        if (day === 0) {
            diff = -6;
        } else {
            diff = ((day - 1) * -1);
        }

        return addDaysToDate(dt, diff);
    };

    function addDaysToDate(dt, daysToAdd) {
        var newdt = new Date();

        return new Date(newdt.setTime(dt.getTime() + (daysToAdd * 86400000)));
    }

    function htmlEncode(value){
        return $("<div/>").text(value).html();
    }

    function htmlDecode(value){
        return $("<div/>").html(value).text();
    }

    function getSuffix(dd) {
        switch (dd) {
        case 1:
        case 21:
        case 31:
            return "st";
        case 2:
        case 22:
            return "nd";
        case 3:
        case 23:
            return "rd";
        default:
            return "th";
        }
    };

    $.extend(timetable.prototype, {

        init: function () {
            this.buildCache();

            this.bindEvents();

            this.configDate(this.options.date, false);

            this.loadTimetable();
        },

        destroy: function () {
            this.unbindEvents();

            this.$element.removeData();
        },

        buildCache: function () {
            this.$element = $(this.element);

            this.$dateTimeContainer = $(this.options.dateTextId);
            this.$resourceSelect = $(this.options.resourceSelectId);
            this.$statusSelect = $(this.options.statusSelectId);
            this.$prevWeekButton = $(this.options.previousWeekButtonId);
            this.$nextWeekButton = $(this.options.nextWeekButtonId);
            this.$dayButtons = $(this.options.dayButtonSelector);
            this.$tableBody = $(this.options.tableId);

            this.allowPrevLink = false;
            this.allowNextLink = true;
        },

        bindEvents: function () {
            var plugin = this;

            plugin.$resourceSelect.on("change", function() {

                plugin.options.filterResourceId = null;

                var selectedValue = plugin.$resourceSelect.val();

                if (selectedValue !== "") {
                    plugin.options.filterResourceId = parseInt(selectedValue);
                }

                plugin.filterRows();
            });

            plugin.$statusSelect.on("change", function () {

                plugin.options.filterStatusId = null;

                var selectedValue = plugin.$statusSelect.val();

                if (selectedValue !== "") {
                    plugin.options.filterStatusId = parseInt(selectedValue);
                }

                plugin.filterRows();
            });

            plugin.$prevWeekButton.on("click", function(event) {
                event.preventDefault();

                if (plugin.allowPrevLink === false) {
                    return;
                }

                var allWeek = (plugin.options.currentDay == null);

                plugin.configDate(addDaysToDate(plugin.options.date, -7), allWeek);

                plugin.loadTimetable();
            });

            plugin.$nextWeekButton.on("click", function (event) {
                event.preventDefault();

                if (plugin.allowNextLink === false) {
                    return;
                }

                var allWeek = (plugin.options.currentDay == null);
                var date;

                if (allWeek) {
                    date = plugin.options.weekDate;
                } else {
                    date = plugin.options.date;
                }

                plugin.configDate(addDaysToDate(date, 7), allWeek);

                plugin.loadTimetable();
            });

            plugin.$dayButtons.on("click", function (event) {
                event.preventDefault();

                var $this = $(this);

                var day = $this.data("day");

                if ($.isNumeric(day) === false) {
                    return;
                }

                var dayDiff = parseInt(day);

                var allWeek = (dayDiff === -1);
                var dt = (allWeek) ? plugin.options.weekDate : addDaysToDate(plugin.options.weekDate, dayDiff);

                if (allWeek === true && plugin.options.currentDay === null)
                    return;

                if (plugin.options.currentDay === dayDiff)
                    return;

                if (allWeek === false) {
                    var today = new Date();

                    if (dt.setHours(0, 0, 0, 0) < today.setHours(0, 0, 0, 0)) {
                        // Move to the following week
                        dt = addDaysToDate(dt, 7);
                    }
                }

                plugin.configDate(dt, allWeek);

                plugin.loadTimetable();
            });

        },

        unbindEvents: function () {
            var plugin = this;

            plugin.$resourceSelect.off("change");

            plugin.$statusSelect.off("change");

            plugin.$prevWeekButton.off("click");

            plugin.$nextWeekButton.off("click");

            plugin.$dayButtons.off("click");
        },

        filterRows: function () {

            var plugin = this;

            plugin.$tableBody.find("tr").each(function (i, row) {
                var $row = $(row),
                    statusId = $row.data("status"),
                    resourceId = $row.data("resource");

                if (plugin.options.filterResourceId != null && resourceId !== plugin.options.filterResourceId) {
                    $row.hide();
                }
                else if (plugin.options.filterStatusId != null && statusId !== plugin.options.filterStatusId) {
                    $row.hide();
                } else {
                    $row.show();
                }

            });

        },

        configDate: function (dt, showWeek) {

            var plugin = this;

            if (dt === null || isValidDate(dt) === false) {
                dt = new Date();
            }

            plugin.options.date = dt;
            plugin.options.weekDate = getStartOfWeek(dt);

            var today = new Date();
            var startOfThisWeek = getStartOfWeek(today);

            if (plugin.options.weekDate.setHours(0, 0, 0, 0) <= startOfThisWeek.setHours(0, 0, 0, 0)) {
                plugin.allowPrevLink = false;
            } else {
                plugin.allowPrevLink = true;
            }

            var latestDate = plugin.options.publishTo;

            if (latestDate === null) {
                plugin.allowNextLink = false;
                plugin.allowNextLink = false;
            } else {
                latestDate = getStartOfWeek(latestDate);

                if (plugin.options.weekDate.setHours(0, 0, 0, 0) >= latestDate.setHours(0, 0, 0, 0)) {
                    plugin.allowNextLink = false;
                } else {
                    plugin.allowNextLink = true;
                }
            }

            plugin.$dateTimeContainer.attr("datetime", formatIsoDateString(dt));

            var html = "Times for " + formatHtmlDateString(dt, !showWeek);

            if (showWeek === true) {
                plugin.options.currentDay = null;

                html += " to " + formatHtmlDateString(addDaysToDate(plugin.options.weekDate, 6), !showWeek);
            } else {
                var day = dt.getDay();

                plugin.options.currentDay = (day === 0) ? 6 : (day - 1);
            }

            plugin.$dateTimeContainer.html(html);

            plugin.$dayButtons.removeClass("active");

            var idx = (showWeek) ? 7 : plugin.options.currentDay;

            if (idx < plugin.$dayButtons.length) {
                plugin.$dayButtons.eq(idx).addClass("active");
            }
        },

        removeTableRows: function () {
            var plugin = this;

            plugin.$tableBody.empty();
        },

        buildTableRow: function(data) {

            var html;

            html = '<tr data-resource="' +
                data.ResourceId +
                '" data-status="' +
                data.StatusId +
                '">' +
                '<td class="dayColumn">' +
                getDayName(data.Day) +
                "</td>" +
                "<td>" +
                data.TimeSlot +
                "</td>";

            if (data.ResourceTip) {
                html += '<td class="session-name"><span class="has-tip" data-tooltip aria-haspopup="true" title="' +
                    htmlEncode(data.ResourceTip) +
                    '">' +
                    htmlEncode(data.ResourceName) +
                    "</span></td>";
            } else {
                html += '<td class="session-name">' + htmlEncode(data.ResourceName) + "</td>";
            }


            if (data.Status) {

                html += '<td class="session-name"><span class="' +
                    data.StatusCssClass +
                    '">' +
                    htmlEncode(data.Status) +
                    "</span></td>";
            } else {
                html += '<td class="session-name"><span></span></td>';
            }

            if (data.ActionHref) {
                var cssClass = "tiny button fullWidth";

                if (data.ActionCssClass) {
                    cssClass += " " + data.ActionCssClass;
                }

                html += '<td class="text-center"><a href="' +
                    data.ActionHref +
                    '" class="' + cssClass + '" data-scheduleid="' + data.ScheduleId + '">' +
                    htmlEncode(data.ActionName) +
                    "</a></td>";
            } else {
                html += '<td class="session-name">&nbsp;</td>';
            }

            html += "</tr>";

            return html;
        },

        setLoadError: function(errorMessage) {
            var plugin = this;

            var columns = (plugin.options.currentDay == null) ? 5 : 4;

            var html = '<tr><td colspan="' + columns + '">' + errorMessage + "</td></tr>";

            plugin.$tableBody.html(html);
        },

        loadTimetable: function() {

            var plugin = this;

            plugin.removeTableRows();

            if ($.isNumeric( plugin.options.setId ) === false) {
                setLoadError("No schedule identifier has been set");
                return;
            }

            if (plugin.options.getTimetableUrl == null) {
                setLoadError("No load url has been set");
                return;
            }

            var loadData = {
                scheduleSetId: plugin.options.setId,
                date: formatIsoDateString(plugin.options.date),
                allWeek: (plugin.options.currentDay === null) ? true : false
            };

            $.ajax({
                type: "POST",
                url: plugin.options.getTimetableUrl,
                data: JSON.stringify(loadData),
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                cache: false,
                success: function (data) {
                    if (data == null) {
                        setLoadError("An error has occurred loading the schedule");
                        return;
                    }

                    // loop through
                    var d = [];

                    $.each(data, function (i, item) {
                        d.push(plugin.buildTableRow(item));
                    });

                    plugin.$tableBody.html(d.join(""));
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    plugin.setLoadError("A problem prevented the schedule from being loaded: " + textStatus);
                },
                complete: function () {

                    if (plugin.options.currentDay === null) {
                        $(plugin.options.dayColumnSelector).show();
                    } else {
                        $(plugin.options.dayColumnSelector).hide();
                    }

                    $(document).foundation("tooltip", "reflow");

                    if (plugin.options.filterResourceId !== null) {
                        plugin.filterRows();
                    }
                }
            });
        }

    });

    $.fn.timetable = function (options) {
        this.each(function () {
            if (!$.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName, new timetable(this, options));
            }
        });

        return this;
    };

    $.fn.timetable.defaults = {
        setId: null,
        date: null,
        weekDate: null,
        dayColumnSelector: ".dayColumn",
        dayButtonSelector: ".timetable-days .button",
        resourceSelectId: "#resource",
        statusSelectId: "#bookingFilter",
        tableId: "#scheduleTable tbody",
        nextWeekButtonId: "#nextweekbtn",
        previousWeekButtonId: "#prevweekbtn",
        dateTextId: "div.timetable-current-day time",
        getTimetableUrl: null,
        filterResourceId: null,
        filterStatusId: null,
        currentDay: null,
        publishTo: null
    };

})(jQuery, window, document);
