// ---------------------------------------------
// ---------- jquery.pestAppointments ----------
// ---------------------------------------------
// Controls the pest appointments table loading
// ---------------------------------------------

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

    var pluginName = "pestAppointments";

    function pestAppointments(element, options) {
        this.element = element;
        this._name = pluginName;
        this._defaults = $.fn.pestAppointments.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 getCalendarHtml(dt) {

        var isoDate = formatIsoDateString(dt);

        var day = dt.getDay();
        var dd = dt.getDate();
        var mm = dt.getMonth();
        var monthName = getMonthName(mm);
        var hiddenMonthPart = getHiddenMonthNamePart(mm);

        var dayName = getDayName(day);
        var hiddenDayPart = getHiddenDayNamePart(day);

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

        var html = '<div class="dateCalBlock noMargin">\r\n' +
            '<span class="dateCal">\r\n' +
            "<span>\r\n" +
            '<time datetime="' + isoDate + '">' +
            '<span class="dateMonthAbbr">' + monthName + '<span class="visuallyhidden">' + hiddenMonthPart + '</span></span><span class="dateDayNum"> ' + dd + " </span></time>\r\n" +
            "</span>\r\n" +
            "</span>\r\n" +
            '<span class="dateCalCommenceString marginTopMedium hide-for-medium-down">' + dayName + hiddenDayPart + "</span>\r\n" +
            '<span class="dateCalCommenceString marginTopMedium show-for-medium-down">' + dayName + "</span>\r\n" +
            "</div>";

        return html;
    }

    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 = getHiddenDayNamePart(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 getHiddenDayNamePart(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(pestAppointments.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.$weekContainer = $(this.options.weekTextSelector);
            this.$prevWeekButtons = $(this.options.previousWeekButtonSelector);
            this.$nextWeekButtons = $(this.options.nextWeekButtonSelector);
            this.$tableBody = $(this.options.tableId);
            this.$slotField = $(this.options.slotFieldId);
            this.$formSelector = $(this.options.formSelector);

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

        bindEvents: function () {
            var plugin = this;

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

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

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

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

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

                plugin.loadTimetable();
            });

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

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

                var date = plugin.options.weekDate;

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

                plugin.loadTimetable();
            });

        },

        unbindEvents: function () {
            var plugin = this;

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

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

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

        setSlot: function($btn) {

            var plugin = this;

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

            plugin.$slotField.val(key);

            plugin.$formSelector.submit();
        },

        configDate: function (dt) {

            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 earliestDate = plugin.options.earliestDate;

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

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

            var longDate = formatHtmlDateString(plugin.options.weekDate, true);
            var shortDate = formatHtmlDateString(plugin.options.weekDate, false);

            var html = '<p class="noBottomMargin hide-for-medium-down">Week starting ' + longDate + "</p>\r\n" +
                       '<p class="text-center show-for-medium-down">Week starts ' + shortDate + "</p>\r\n";

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

        removeTableRows: function () {
            var plugin = this;

            plugin.$tableBody.empty();
        },

        buildTableRow: function(data) {

            var plugin = this;

            var yy = parseInt(data.Date.substr(0, 4));
            var mm = parseInt(data.Date.substr(5, 2));
            var dd = parseInt(data.Date.substr(8, 2));

            if (isNaN(yy) || isNaN(mm) || isNaN(dd))
                return "";

            mm = mm - 1;

            var dt = new Date(yy, mm, dd);

            var key = data.Key;

            var timeSlot = data.TimeSlot;
            var period = data.Period;

            var html = "<tr>" +
                "<td>" +
                getCalendarHtml(dt) +
                "</td>" +
                '<td class="text-center">' +
                timeSlot +
                "</td>" +
                '<td class="text-center">' +
                '<a href="#" class="tiny button fullWidth appointmentBookBtn" data-key="' + key + '">Book</a>' +
                "</td>";

            return html;
        },

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

            var columns = 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);
        },

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

            $.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) {
                        plugin.setLoadError("An error has occurred loading the timetable");
                        return;
                    }

                    if (typeof data === "string") {
                        plugin.setLoadError(data);
                        return;
                    }

                    if (typeof data === "undefined") {
                        plugin.setLoadError("An error has occurred whilst retrieving the appointment slots");
                        return;
                    }

                    if (data.length === 0) {
                        plugin.setLoadError('No available slots found for your selected period<br><span class="smaller">Try another week</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 () {
                    $(plugin.options.bookButtonSelector).on("click", function (event) {
                        event.preventDefault();

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

    });

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

        return this;
    };

    $.fn.pestAppointments.defaults = {
        date: null,
        weekDate: null,
        slotFieldId: "#selectedSlot",
        bookButtonSelector: ".appointmentBookBtn",
        tableId: "#timetableTable tbody",
        nextWeekButtonSelector: ".nextWeekBtn",
        previousWeekButtonSelector: ".prevWeekBtn",
        weekTextSelector: ".weekText",
        formSelector: "#frmPest",
        getTimetableUrl: null,
        earliestDate: null
    };

})(jQuery, window, document);
