// -----------------------------------------------------
// ---------- jquery.residentPermitSlots ---------------
// -----------------------------------------------------
// Controls the resident permit timetable table loading
// -----------------------------------------------------

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

    var pluginName = "residentPermitSlots";

    function residentPermitSlots(element, options) {
        this.element = element;
        this._name = pluginName;
        this._defaults = $.fn.residentPermitSlots.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 getMonthName(month) {
        var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

        return monthNames[month];
    }

    function getHiddenMonthNamePart(month) {
        var monthNames = ["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"];

        return monthNames[month];
    }

    function formatHtmlMonthString(dt) {
        var mm = dt.getMonth();
        var monthName = getMonthName(mm);
        var hiddenMonthPart = getHiddenMonthNamePart(mm);

        if (hiddenMonthPart === "") {
            return monthName;
        }

        return monthName +
            '<span class="visuallyhidden">' +
            hiddenMonthPart +
            "</span>";
    }

    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;
    };

    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";
        }
    };

    function isNullOrWhiteSpace(str) {
        if (typeof str === "undefined" ||
            !str ||
            str.length === 0 ||
            !/[^\s]/.test(str) ||
            /^\s*$/.test(str) ||
            str.replace(/\s/g, "") === "") {
            return true;
        } else {
            return false;
        }
    }

    $.extend(residentPermitSlots.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.$monthTextContainer = $(this.options.monthTextId);
            this.$dayTextContainer = $(this.options.dayTextId);
            this.$timeElement = $(this.options.dateAttributeObject);
            this.$prevWeekButton = $(this.options.previousWeekButtonId);
            this.$nextWeekButton = $(this.options.nextWeekButtonId);
            this.$dayButtons = $(this.options.dayButtonSelector);
            this.$tableBody = $(this.options.tableId);
            this.$slotField = $(this.options.slotFieldId);
            this.$formSelector = $(this.options.formSelector);

            if (this.options.datePickerFieldId !== null && this.options.picker !== null) {
                this.$datePickerField = $(this.options.datePickerFieldId);
            } else {
                this.$datePickerField = null;
            }

            if (this.options.minDate === null) {
                this.options.minDate = new Date();
            }

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

        bindEvents: function () {
            var plugin = this;

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

                plugin.setSlot($(this));
            });

            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.setDatePickerDate();

                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.setDatePickerDate();

                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 eDate = new Date();

                    if (plugin.options.minDate !== null) {
                        eDate = plugin.options.minDate;
                    }

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

                plugin.configDate(dt, allWeek);

                plugin.setDatePickerDate();

                plugin.loadTimetable();
            });

            if (plugin.$datePickerField === null) {
                return;
            }

            plugin.$datePickerField.on("change", function (event) {
                var dtSetting = plugin.options.picker.get("select");

                if (plugin.ignoreDateSet === true) {
                    return;
                }

                if (typeof dtSetting === "object") {
                    if (dtSetting.obj !== plugin.options.date) {
                        plugin.configDate(dtSetting.obj, false);

                        plugin.loadTimetable();
                    }
                }
            });
        },

        unbindEvents: function () {
            var plugin = this;

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

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

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

            if (plugin.$datePickerField === null) {
                return;
            }

            plugin.$datePickerField.off("change");
        },

        setDatePickerDate: function () {
            var plugin = this;

            plugin.ignoreDateSet = true;

            if (plugin.options.onDateChange !== null) {
                plugin.options.onDateChange(formatIsoDateString(plugin.options.date));
            }

            plugin.ignoreDateSet = false;
        },

        configDate: function (dt, showWeek) {

            var plugin = this;

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

            var today = new Date();

            if (dt.setHours(0, 0, 0, 0) < plugin.options.minDate.setHours(0, 0, 0, 0)) {
                dt = plugin.options.minDate;
            }

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

            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;

                if (plugin.options.minDate.setHours(0, 0, 0, 0) > dt.setHours(0, 0, 0, 0)) {
                    plugin.allowPrevLink = false;
                }
            }

            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.$timeElement.attr("datetime", formatIsoDateString(dt));

            var html = 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.$dayTextContainer.html(" " + dt.getDate() + " ");

            plugin.$monthTextContainer.html(formatHtmlMonthString(dt));

            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 plugin = this;

            var key = data.Key;

            var html = "<tr>" +
                '<td class="dayColumn">' +
                getDayName(data.Day) +
                "</td>" +
                '<td class="text-center">' +
                data.TimeSlot +
                "</td>";

            if (data.Slots > 0) {
                html += '<td class="session-name"><span class="success label">Available</span></td>' +
                    '<td class="text-center">' +
                    '<a href="#" class="tiny button fullWidth appointmentBookBtn" data-key="' +
                    key +
                    '">Book</a>' +
                    "</td>";
            } else {
                html += '<td class="session-name"><span class="alert label">Fully Booked</span></td>' +
                    '<td class="text-center"></td>';
            }

            html += "</tr>";

            return html;
        },

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

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

            var html = '<tr><td colspan="' + columns + '"><p><span class="warning expanded label largeText paddingAll radius">' + errorMessage + "</span></p></td></tr>";

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

        setSlot: function ($btn) {

            var plugin = this;

            var key = $btn.data("key");

            plugin.$slotField.val(key);

            plugin.$formSelector.submit();
        },

        loadTimetable: function() {

            var plugin = this;

            plugin.removeTableRows();

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

            var loadData = {
                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 || typeof data === "undefined") {
                        plugin.setLoadError("An error has occurred loading the timetable");
                        return;
                    }

                    if (typeof data === "string") {
                        plugin.setLoadError(data + '<br><span class="smaller">Try another date or duration</span>');
                        return;
                    }

                    if (data.length === 0) {
                        plugin.setLoadError('No available slots found for your selected period<br><span class="smaller">Try another date or duration</span>');
                        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 timetable from being loaded: " + textStatus);
                },
                complete: function () {

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

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

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

                        plugin.setSlot($(this));
                    });
                }
            });
        }

    });

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

        return this;
    };

    $.fn.residentPermitSlots.defaults = {
        date: null,
        weekDate: null,
        minDate: null,
        slotFieldId: "#selectedSlot",
        dayColumnSelector: ".dayColumn",
        dayButtonSelector: ".timetable-days .button",
        tableId: "#timetableTable tbody",
        nextWeekButtonId: "#nextweekbtn",
        previousWeekButtonId: "#prevweekbtn",
        dateTextId: "span.dateCalCommenceString",
        bookButtonSelector: ".appointmentBookBtn",
        dateAttributeObject: "time",
        monthTextId: "span.dateMonthAbbr",
        dayTextId: "span.dateDayNum",
        formSelector: "#frmWaste",
        getTimetableUrl: null,
        currentDay: null,
        publishTo: null,
        onDateChange: null,
        datePickerFieldId: null,
        picker: null
    };

})(jQuery, window, document);
