$(function() {

    if (typeof window.VH == "undefined" || !window.VH) {
        window.VH = {};
    }

    if (typeof VH.CAL == "undefined" || !VH.CAL) {
        VH.CAL = {};
    }

    /**
    * Object that controls the calenders and booking form elements.
    * 
    * requires;
    * - vh.util.js
    * - vh.gui.js
    */

    VH.CAL = function() {

        var self = this;

        // constants
        var TODAY = new Date();
        var ONE_DAY = 86400000;
        var _leavingDate, _returningDate, _duration, _minDur, _maxDur;



        // object refs
        var util = null;
        var gui = null;

        /**
        * UTIL METHODS
        */

        var daysFromNow = function(days) {
            return new Date(TODAY.getTime() + (ONE_DAY * days));
        };

        var yearsFromNow = function(years) {
            var year = TODAY.getFullYear() + years;
            return new Date("12/31/" + year);
        };

        /**
        * SETTING PROPERTIES
        */

        var _data = {
            RouteData: null,
            EarliestBookableDate: daysFromNow(2),
            LatestBookableDate: yearsFromNow(1),
            FixedDuration: null
        };

        var _defaultOptions = function() {
            var o = {
                // basic options
                rangeSelect: false, // no date range selection
                numberOfMonths: 1, // no. of calendars to show
                /*yearRange: '-0:+1', // sets to current year + 1*/
                firstDay: 1, // sets to Monday
                changeFirstDay: false, // disable links on days of week
                hideIfNoPrevNext: false, // hide links
                speed: '', // just show it

                // min, max and formatting
                minDate: _data.EarliestBookableDate,
                maxDate: _data.LatestBookableDate,
                dateFormat: 'D d M yy',
                dayNamesMin: ['S', 'M', 'T', 'W', 'T', 'F', 'S']
            };
            return o;
        };

        var _returnCalOptions = function() {
            var o = {
                defaultDate: _returningDate,
                minDate: new Date((_leavingDate.getTime() + (ONE_DAY * _minDur))),
                maxDate: new Date((_leavingDate.getTime() + (ONE_DAY * _maxDur)))
            };
            return o;
        };

        var setData = function(externalData) {

            // min date
            if (!util.isUndefinedOrNull(externalData.EarliestBookableDate)) {
                _data.EarliestBookableDate = new Date(externalData.EarliestBookableDate);
            }
            // max date
            if (!util.isUndefinedOrNull(externalData.LatestBookableDate)) {
                _data.LatestBookableDate = new Date(externalData.LatestBookableDate);
            }
            // route data 
            if (!util.isUndefinedOrNull(externalData.RouteData)) {
                _data.RouteData = externalData.RouteData;
            }
            // fixed duration
            if (!util.isUndefinedOrNull(externalData.FixedDuration)) {
                _data.FixedDuration = externalData.FixedDuration;
            }

            util.log("Route Data - %o", _data);

        };

        var setLeavingDate = function(lDate) {

            util.log(lDate);
            util.log(gui.SearchForm.Leaving.val());

            if (util.isUndefinedOrNull(lDate)) {
                
                // use default
                _leavingDate = new Date(gui.SearchForm.Leaving.val());

                // use default
                _leavingDate = new Date(gui.SearchForm.Leaving.val());

                // earliest bookable date is well past the default leaving date
                if (_data.EarliestBookableDate > _leavingDate) {
                    _leavingDate = _data.EarliestBookableDate
                }

                // leaving date is past the latest bookable date                
                if (_leavingDate > _data.LatestBookableDate) {
                    _leavingDate = daysFromNow(14);
                }
                
            } else {
                _leavingDate = new Date(lDate);
            }

        };

        var setReturningDate = function(rDate) {

            if (util.isUndefinedOrNull(rDate)) {
                _returningDate = new Date(_leavingDate.getTime() + (_duration * ONE_DAY));
            } else {
                _returningDate = rDate;
            }
            _returnMonth = _returningDate.getMonth() + 1;

        };

        var setDuration = function(duration) {
            if (util.isUndefinedOrNull(duration)) {
                // use default
                _duration = parseInt(gui.Calendar.Duration.val(), 10);
            } else {
                _duration = parseInt(duration, 10);
                gui.Calendar.Duration.val(_duration);
            }
            util.log("New Duration       - " + _duration);
            $("#DurationsDiv select").val($(".durationFs :selected").val());
        };


        /**
        *  SHOW / HIDE CALENDAR
        */

        var OnShowCalendar = function() {

            close_myitinerary()
            var airportExists = gui.SearchForm.Airport.length > 0;
            var userHasntSelectedAirport = gui.SearchForm.Airport.val() === "-";

            if (airportExists && userHasntSelectedAirport) {

                gui.SearchForm.NoAirport.html("Please select a departure airport.");

            } else {

                // swap z-indexes
                gui.Page.Browse.css({ 'z-index': '800' });
                gui.Page.Search.css({ 'z-index': '999' });

                // oh dear
                var isOldIE = (jQuery.browser.msie && jQuery.browser.version < 7);
                if (isOldIE) {
                    $("fieldset.who").find("select").css({ 'visibility': 'hidden' });
                    $("#DurationsDiv").find("select").css({ 'visibility': 'hidden' });
                }

                // show for instant response
                // gui.Page.Calendar.show();
                gui.Page.Calendar.css({ "display": "block" });


            }

            ApplyRouteData(gui.Calendar.LeavingCal);
            ApplyRouteData(gui.Calendar.ReturningCal);

        };

        var OnHideCalendar = function() {

            var gui = VH.GUI;


            // cant close if it is inline, but do need to make sure that route data is updated.
            if (gui.Page.Calendar.hasClass("inline")) {
                OnShowCalendar();
                return;
            };

            gui.Page.Browse.css({ 'z-index': '80' });
            gui.Page.Search.css({ 'z-index': '70' });

            // set new leaving date & duration
            gui.SearchForm.Duration.val(gui.Calendar.Duration.val());
            gui.SearchForm.Leaving.val($.datepicker.formatDate('D d M yy', _leavingDate));


            var isOldIE = (jQuery.browser.msie && jQuery.browser.version < 7);
            if (isOldIE) {
                $("fieldset.who").find("select").css({ 'visibility': 'visible' });
                $("#DurationsDiv").find("select").css({ 'visibility': 'visible' });
            }

            gui.Page.Calendar.hide();

        };


        /**
        *  INITIALISATION
        */


        // override departure date by 2 weeks; 17th Dec 2006
        var overrideDepartureDate = function() {

            // has the user already done a search?
            var rsCookie = $.cookie("RememberedSearch");
            if (VH.UTIL.isUndefinedOrNull(rsCookie)) {

                // no, lets set up the default date - 2 weeks from now
                var twoWeeksFromNow = $.datepicker.formatDate('D d M yy', daysFromNow(14));
                gui.SearchForm.Leaving.val(twoWeeksFromNow);

            }

        };

        var Init = function(data) {

            util = VH.UTIL;
            gui = VH.GUI;

            // clear any error messages
            gui.SearchForm.NoAirport.html("");


            overrideDepartureDate();


            // determine initial data source
            if (util.isUndefinedOrNull(data)) {
                if (!util.isUndefinedOrNull(window.CALENDAR_DATA)) {
                    util.log("setting route data from CALENDAR_DATA");
                    setData(CALENDAR_DATA);
                }
            } else {
                util.log("setting route data from arg");
                setData(data);
            }

            // FIX - override for IE (until no-cache on next release)
            var cookieValue = $.cookie("RememberedSearch");
            if (!VH.UTIL.isUndefinedOrNull(cookieValue)) {
                var query = $.query(cookieValue);
                depDate = query.DepartureDate;
                VH.GUI.SearchForm.Leaving.val(depDate);
            }

            // set min and max duration
            var allOptions = gui.Calendar.Duration.find("option");
            _minDur = parseInt(allOptions.eq(0).attr("value"), 10);
            _maxDur = parseInt(allOptions.eq(allOptions.length - 1).attr("value"), 10);


            // init departure dropdown if data was passed in via AJAX,
            // otherwise, they are set via .NET
            if (!util.isUndefinedOrNull(data)) {
                initAirports();
            }

            // init calendars
            initCalendars();

            // init gui / form elements
            initControls();



            // check duration, if its fixed, hide duration dropdown
            if (!util.isUndefinedOrNull(_data.FixedDuration)) {
                gui.Calendar.Duration.hide();
                $("#durationsCalFixed").show().text(_data.FixedDuration + " nights")
            }

        };

        var initAirports = function() {

            var airportsDD = gui.SearchForm.Airport;

            if (!util.isUndefinedOrNull(_data.RouteData)) {



                airportsDD.attr("disabled", true).empty();

                var options = '' //<option value="-">Please Select</option>';
                for (var i = 0, len = _data.RouteData.length; i < len; i++) {
                    var rd = _data.RouteData[i];
                    var airportCode = rd.FromAirportCode;
                    var airportName = rd.FromAirportName;
                    options += '<option value="' + airportCode + '">' + airportName + '</option>';
                }

                airportsDD.empty().append(options).attr("disabled", false);

                // can we select the users airport from the last search?
                cookieValue = $.cookie("RememberedSearch");
                if (!util.isUndefinedOrNull(cookieValue)) {
                    cookieQuery = $.query(cookieValue);
                    if (!util.isUndefinedOrNull(cookieQuery.DepartureAirport)) {
                        var rememberedAirport = cookieQuery.DepartureAirport;
                        airportsDD.val(rememberedAirport);
                    }
                }


            }

        };

        var initCalendars = function() {

            // set duration / dates
            setDuration(gui.SearchForm.Duration.val());
            gui.Calendar.Duration.val(_duration);
            setLeavingDate();
            setReturningDate();

            // init calendars	
            initLeavingCal();
            initReturningCal();

        };

        var initLeavingCal = function() {

            if (!util.isUndefinedOrNull(gui.Calendar.LeavingCal.datepicker)) {
                gui.Calendar.LeavingCal.datepicker("destroy");
            }

            // create leaving options
            var leavingOptions = {};
            $.extend(leavingOptions, _defaultOptions(), {
                defaultDate: _leavingDate,
                onSelect: OnChangeLeavingDate, /* date selected */
                onRender: OnRenderLeavingCal, /* calendar redrawn */
                onChangeMonthYear: OnChangedLeavingMonthYear /* month / year selected */
            });

            gui.Calendar.LeavingCal.datepicker(leavingOptions);

        };

        var initReturningCal = function() {

            if (!util.isUndefinedOrNull(gui.Calendar.ReturningCal.datepicker)) {
                gui.Calendar.ReturningCal.datepicker("destroy");
            }

            var returningOptions = {};
            $.extend(returningOptions, _defaultOptions(), _returnCalOptions(), {
                onSelect: OnChangeReturningDate, /* date selected */
                onRender: OnRenderReturningCal, /* calendar redrawn */
                onChangeMonthYear: OnChangedReturnMonthYear /* month / year selected */
            });

            gui.Calendar.ReturningCal.datepicker("destroy");
            gui.Calendar.ReturningCal.datepicker(returningOptions);



        };

        var initControls = function() {

            // set focus for date on search form
            gui.SearchForm.Leaving.focus(function() {
                $(this).blur();
                VH.CAL.OnShowCalendar();
            });

            // icon on search form
            gui.SearchForm.Icon.click(function() {
                VH.CAL.OnShowCalendar();
            });

            // duration on search form
            gui.SearchForm.Duration.change(function(e) {
                VH.CAL.OnSearchFormDurationChanged($(this).val());
            });

            // duration on calendar
            gui.Calendar.Duration.change(function(e) {
                VH.CAL.OnDurationChanged($(this).val());
            });

            // cancel on calendar
            gui.Calendar.Cancel.click(function(e) {
                e.preventDefault();
                VH.CAL.OnHideCalendar();
            });

            // ok btn
            $("#calOkBtn").click(function(e) {
                e.preventDefault();
                var isOldIE = (jQuery.browser.msie && jQuery.browser.version < 7);
                if (isOldIE) {
                    $("fieldset.who").find("select").css({ 'visibility': 'visible' });
                    $("#DurationsDiv").find("select").css({ 'visibility': 'visible' });
                }
                VH.CAL.OnHideCalendar();
            });

            // airport change on search form
            gui.SearchForm.Airport.change(function(e) {
                VH.CAL.OnHideCalendar();
                gui.SearchForm.NoAirport.html("");
                VH.CAL.OnInitCalendars();
            });

        };

        var fixDisplay = function(calendar) {

            var selects = calendar.find("select");
            selects.eq(0).css({ "width": "100px" }); // month
            selects.eq(1).css({ "width": "60px" }); // year

            // prev/next labels
            calendar.find(".ui-datepicker-prev label:empty").hide();
            calendar.find(".ui-datepicker-next label:empty").hide();

        };

        /**
        * HIGHLIGHTING 
        */

        // highlight
        var doHighlight = function(calendar) {


            // find all day cells
            var days = calendar.find("td.ui-datepicker-days-cell");

            var lDate = null;
            var rDate = null;
            var lDateTxt = $.datepicker.formatDate('D d M yy', _leavingDate);
            var rDateTxt = $.datepicker.formatDate('D d M yy', _returningDate);

            days.each(function(i) {

                // clear any highlighting
                $(this).removeClass("ui-datepicker-inrange");
                $(this).removeClass("ui-datepicker-days-cell-over");

                var cellDate = new Date($(this).find("span").text());
                var cDateTxt = $.datepicker.formatDate('D d M yy', cellDate);

                if (cDateTxt === lDateTxt) {
                    lDate = {
                        jq: $(this),
                        index: i
                    };
                }
                if (cDateTxt === rDateTxt) {
                    rDate = {
                        jq: $(this),
                        index: i + 1
                    };
                }
            });

            // both dates found
            if (lDate !== null && rDate !== null) {
                util.log("both dates found... [" + calendar[0].id + "]");
                days.slice(lDate.index, rDate.index).addClass("ui-datepicker-inrange");
            }

            // only leaving date found
            if (lDate !== null && rDate === null) {
                util.log("only leaving date found... [" + calendar[0].id + "]");
                days.slice(lDate.index, days.length).addClass("ui-datepicker-inrange");
            }

            // only returning date found
            if (lDate === null && rDate !== null) {
                util.log("only returning date found... [" + calendar[0].id + "]");
                var start = 1;
                if (rDate.index - (_duration + 1) >= 0) {
                    start = rDate.index - (_duration + 1);
                }
                days.slice(start, rDate.index).addClass("ui-datepicker-inrange");
            }

            // clear other month highlighting
            var otherMonthDays = calendar.find("td.ui-datepicker-other-month");
            otherMonthDays.removeClass("ui-datepicker-inrange");

        };

        var updateCalendarHighlighting = function() {
            doHighlight(gui.Calendar.LeavingCal);
            doHighlight(gui.Calendar.ReturningCal);
        };

        var updateCalendarText = function() {
            gui.Calendar.Leaving.html($.datepicker.formatDate('D d M yy', _leavingDate));
            gui.Calendar.Returning.html($.datepicker.formatDate('D d M yy', _returningDate));
        };

        var OnInitCalendars = function() {
            initLeavingCal();
            initReturningCal();
        }

        var OnInitCalendars = function() {
            initLeavingCal();
            initReturningCal();
        }

        /**
        * DURATION CALLBACKS
        */

        var OnDurationChanged = function(duration) {
            util.log("Duration Changed (" + duration + ")");
            setDuration(duration);
            setReturningDate();
            initReturningCal();
            updateCalendarHighlighting();
            // auto update form
            gui.SearchForm.Duration.val(duration);



        };

        var OnSearchFormDurationChanged = function(duration) {
            initCalendars();
        };

        /**
        * LEAVING CALENDAR CALLBACKS
        */

        var OnRenderLeavingCal = function() {
            util.log("OnRenderLeavingCal...");

            // show dates as text
            updateCalendarText();
            doHighlight(gui.Calendar.LeavingCal);
            fixDisplay(gui.Calendar.LeavingCal);
            ApplyRouteData(gui.Calendar.LeavingCal);
            ApplyRouteData(gui.Calendar.LeavingCal);


        };

        var OnChangeLeavingDate = function(date) {
            util.log("OnChangeLeavingDate...");
            setLeavingDate(new Date(date));
            setReturningDate();
            initReturningCal();
            // auto update form
            gui.SearchForm.Leaving.val($.datepicker.formatDate('D d M yy', _leavingDate));
        };

        var OnChangedLeavingMonthYear = function(year, month) {
            util.log("OnChangedLeavingMonthYear...");
        };

        /**
        * RETURN CALENDAR CALLBACKS
        */

        var OnRenderReturningCal = function() {
            util.log("OnRenderReturningCal...");
            updateCalendarText();
            doHighlight(gui.Calendar.ReturningCal);
            fixDisplay(gui.Calendar.ReturningCal);
            ApplyRouteData(gui.Calendar.ReturningCal);
            ApplyRouteData(gui.Calendar.ReturningCal);
        };

        var OnChangeReturningDate = function(date) {
            util.log("OnChangeReturningDate...");
            setReturningDate(new Date(date));

            // find new difference between leaving and return in days
            var newDuration = Math.floor((_returningDate.getTime() - _leavingDate.getTime()) / ONE_DAY);
            setDuration(newDuration);
            gui.Calendar.Duration.val(newDuration);
            initLeavingCal();
            initReturningCal();

            // auto close
            // VH.CAL.OnHideCalendar();
        };

        var OnChangedReturnMonthYear = function(year, month) {
            util.log("OnChangedReturnMonthYear...");
        };

        /**
        * ROUTE DATA
        */

        var ApplyRouteData = function(calendar) {

            util.log("ApplyRouteData:" + calendar.get(0).id);

            var _routeData = GetRouteData();

            if (!util.isUndefinedOrNull(_routeData)) {

                // using route data, remove hrefs from cells
                var calId = calendar[0].id;

                var days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];

                for (j = 0, l = days.length; j < l; j++) {

                    var whichDay = ((calId === "rCal") ? "IsIn" : "IsOut") + days[j];
                    var validDay = _routeData[whichDay];

                    if (validDay === false) {
                        calendar.find(".ui-datepicker-days-row td:nth-child(" + (j + 1) + ")").each(function(i) {
                            var href = $(this).find("a");
                            var txt = href.text();
                            href = href.replaceWith("<b style='color:#888888;font-weight:normal'>" + txt + "</b>");
                            $(this).attr("onclick", "");
                            $(this).attr("onmouseover", "");
                            $(this).attr("onmouseout", "");
                        });
                    }

                }

            }

        };

        var GetRouteData = function() {

            var routeDataPresent = !util.isUndefinedOrNull(_data.RouteData);
            var userSelectedValidAirport = gui.SearchForm.Airport.val() !== "-";

            if (routeDataPresent && userSelectedValidAirport) {

                for (var i = 0, len = _data.RouteData.length; i < len; i++) {
                    var rd = _data.RouteData[i];
                    if (gui.SearchForm.Airport.val() == rd.FromAirportCode) {
                        return rd;
                    }
                }

            }

            return null;
        };

        return {
            Init: Init,
            OnInitCalendars: OnInitCalendars,
            OnShowCalendar: OnShowCalendar,
            OnHideCalendar: OnHideCalendar,
            OnDurationChanged: OnDurationChanged,
            OnSearchFormDurationChanged: OnSearchFormDurationChanged,
            OnRenderLeavingCal: OnRenderLeavingCal,
            OnChangeLeavingDate: OnChangeLeavingDate,
            OnChangeLeavingMonthYear: OnChangedLeavingMonthYear,
            OnChangeReturningDate: OnChangeLeavingDate,
            OnChangedReturnMonthYear: OnChangedReturnMonthYear,
            OnRenderReturningCal: OnRenderReturningCal
        };

    } ();

});
