/*!
 * Piwik - free/libre analytics platform
 *
 * @link http://piwik.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */
(function ($) {

    Date.prototype.getWeek = function () {
        var onejan = new Date(this.getFullYear(), 0, 1), // needed for getDay()

        // use UTC times since getTime() can differ based on user's timezone
            onejan_utc = Date.UTC(this.getFullYear(), 0, 1),
            this_utc = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()),

            daysSinceYearStart = (this_utc - onejan_utc) / 86400000; // constant is millisecs in one day

        return Math.ceil((daysSinceYearStart + onejan.getDay()) / 7);
    };

    var currentYear, currentMonth, currentDay, currentDate, currentWeek;

    function formatDateString(date) {
        var month = date.getMonth() + 1;
        var day = date.getDate();
        if (month < 10) {
            month = '0' + month;
        }
        if (day < 10) {
            day = '0' + day;
        }

        return date.getFullYear() + '-' + month + '-' + day;
    }

    function updateDisplayDate(selectedPeriod, dateText)
    {
        piwik.period = selectedPeriod;

        if (dateText && dateText.indexOf(',') > -1) {
            var dateParts = dateText.split(',');
            if (dateParts[1]) {
                piwik.currentDateString = dateParts[1];
            } else if (dateParts[0]) {
                piwik.currentDateString = dateParts[0];
            }
        } else {
            piwik.currentDateString = dateText;
        }

        if (selectedPeriod === 'week') {
            var millisecondsPerDay = 24 * 60 * 60 * 1000;
            var currentTimeSelected = currentDate.getTime();
            // we fix currentDayOfWeek as our week starts on Monday.
            var currentDayOfWeek = currentDate.getDay();
            if (currentDayOfWeek === 0) {
                // Usually Sunday === 0, we set Sunday to the end of the week.
                currentDayOfWeek = 6;
            } else {
                // we move every day one day forward, so Monday which is usually 1 becomes 0
                currentDayOfWeek = currentDayOfWeek - 1;
            }

            var firstDayOfWeek = new Date();
            var lastDayOfWeek = new Date();
            firstDayOfWeek.setTime(currentTimeSelected - (millisecondsPerDay * currentDayOfWeek));
            lastDayOfWeek.setTime(firstDayOfWeek.getTime() + (millisecondsPerDay * 6));

            piwik.startDateString = formatDateString(firstDayOfWeek);
            piwik.endDateString   = formatDateString(lastDayOfWeek);
        } else if (dateText.indexOf(',') !== -1) {
            var dateParts = dateText.split(',');
            piwik.startDateString = dateParts[0];
            piwik.endDateString = dateParts[1];
        } else {
            piwik.startDateString = dateText;
            piwik.endDateString = dateText;
        }

        var displayDate = dateText;
        if (selectedPeriod === 'month') {
            displayDate = _pk_translate('Intl_Month_Long_StandAlone_' + (currentMonth+1)) + ' ' + currentYear;
        } else if (selectedPeriod === 'year') {
            displayDate = currentYear;
        } else if (selectedPeriod === 'range' || selectedPeriod === 'week') {
            displayDate = _pk_translate('General_DateRangeFromTo', [piwik.startDateString, piwik.endDateString]);
        }

        var $title = $('#date.title');
        $title.text(displayDate);
        $title.attr('title', _pk_translate('General_ChooseDate', [piwikHelper.escape(displayDate)]));

        if (typeof initTopControls !== 'undefined' && initTopControls) {
            initTopControls();
        }
    }

    function setCurrentDate(dateStr) {
        if (dateStr && dateStr.indexOf(',') !== -1) {
            var parts = dateStr.split(',');
            dateStr = parts[0];
        }

        var splitDate = dateStr.split('-');
        currentYear = splitDate[0];
        currentMonth = splitDate[1] - 1;
        currentDay = splitDate[2];
        currentDate = new Date(currentYear, currentMonth, currentDay);
        currentWeek = currentDate.getWeek();
    }

    if(!piwik.currentDateString) {
        // eg. Login form
        return;
    }

    setCurrentDate(piwik.currentDateString);

    var todayDate = new Date;
    var todayMonth = todayDate.getMonth();
    var todayYear = todayDate.getFullYear();
    var todayDay = todayDate.getDate();

    // min/max date for picker
    var piwikMinDate = new Date(piwik.minDateYear, piwik.minDateMonth - 1, piwik.minDateDay),
        piwikMaxDate = new Date(piwik.maxDateYear, piwik.maxDateMonth - 1, piwik.maxDateDay);

    // we start w/ the current period
    var selectedPeriod = piwik.period;

    function isDateInCurrentPeriod(date) {
        // if the selected period isn't the current period, don't highlight any dates
        if (selectedPeriod != piwik.period) {
            return [true, ''];
        }

        var valid = false;

        var dateMonth = date.getMonth();
        var dateYear = date.getFullYear();
        var dateDay = date.getDate();

        // we don't color dates in the future
        if (dateMonth == todayMonth
            && dateYear == todayYear
            && dateDay > todayDay
            ) {
            return [true, ''];
        }

        // we don't color dates before the minimum date
        if (dateYear < piwik.minDateYear
            || ( dateYear == piwik.minDateYear
            &&
            (
                (dateMonth == piwik.minDateMonth - 1
                    && dateDay < piwik.minDateDay)
                    || (dateMonth < piwik.minDateMonth - 1)
                )
            )
            ) {
            return [true, ''];
        }

        // we color all day of the month for the same year for the month period
        if (piwik.period == "month"
            && dateMonth == currentMonth
            && dateYear == currentYear
            ) {
            valid = true;
        }
        // we color all day of the year for the year period
        else if (piwik.period == "year"
            && dateYear == currentYear
            ) {
            valid = true;
        }
        else if (piwik.period == "week"
            && date.getWeek() == currentWeek
            && dateYear == currentYear
            ) {
            valid = true;
        }
        else if (piwik.period == "day"
            && dateDay == currentDay
            && dateMonth == currentMonth
            && dateYear == currentYear
            ) {
            valid = true;
        }

        if (valid) {
            return [true, 'ui-datepicker-current-period'];
        }

        return [true, ''];
    }

    function propagateNewUrlParams(date, period)
    {
        if (piwikHelper.isAngularRenderingThePage()) {

            $('#periodString').removeClass('expanded');
            piwikHelper.hideAjaxLoading('ajaxLoadingCalendar');

            angular.element(document).injector().invoke(function ($location) {
                var $search = $location.search();

                if (date !== $search.date || period !== $search.period) {
                    // eg when using back button the date might be actually already changed in the URL and we do not
                    // want to change the URL again
                    $search.date = date;
                    $search.period = period;
                    $location.search($search);
                }

            });

        } else {
            // Let broadcast do its job:
            // It will replace date value to both search query and hash and load the new page.
            broadcast.propagateNewPage('date=' + date + '&period=' + period);
        }
    }

    piwik.getBaseDatePickerOptions = function (defaultDate) {
        return {
            showOtherMonths: false,
            dateFormat: 'yy-mm-dd',
            firstDay: 1,
            minDate: piwikMinDate,
            maxDate: piwikMaxDate,
            prevText: "",
            nextText: "",
            currentText: "",
            defaultDate: defaultDate,
            changeMonth: true,
            changeYear: true,
            stepMonths: 1,
            // jquery-ui-i18n 1.7.2 lacks some translations, so we use our own
            dayNamesMin: [
                _pk_translate('Intl_Day_Min_StandAlone_7'),
                _pk_translate('Intl_Day_Min_StandAlone_1'),
                _pk_translate('Intl_Day_Min_StandAlone_2'),
                _pk_translate('Intl_Day_Min_StandAlone_3'),
                _pk_translate('Intl_Day_Min_StandAlone_4'),
                _pk_translate('Intl_Day_Min_StandAlone_5'),
                _pk_translate('Intl_Day_Min_StandAlone_6')],
            dayNamesShort: [
                _pk_translate('Intl_Day_Short_StandAlone_7'), // start with sunday
                _pk_translate('Intl_Day_Short_StandAlone_1'),
                _pk_translate('Intl_Day_Short_StandAlone_2'),
                _pk_translate('Intl_Day_Short_StandAlone_3'),
                _pk_translate('Intl_Day_Short_StandAlone_4'),
                _pk_translate('Intl_Day_Short_StandAlone_5'),
                _pk_translate('Intl_Day_Short_StandAlone_6')],
            dayNames: [
                _pk_translate('Intl_Day_Long_StandAlone_7'), // start with sunday
                _pk_translate('Intl_Day_Long_StandAlone_1'),
                _pk_translate('Intl_Day_Long_StandAlone_2'),
                _pk_translate('Intl_Day_Long_StandAlone_3'),
                _pk_translate('Intl_Day_Long_StandAlone_4'),
                _pk_translate('Intl_Day_Long_StandAlone_5'),
                _pk_translate('Intl_Day_Long_StandAlone_6')],
            monthNamesShort: [
                _pk_translate('Intl_Month_Short_StandAlone_1'),
                _pk_translate('Intl_Month_Short_StandAlone_2'),
                _pk_translate('Intl_Month_Short_StandAlone_3'),
                _pk_translate('Intl_Month_Short_StandAlone_4'),
                _pk_translate('Intl_Month_Short_StandAlone_5'),
                _pk_translate('Intl_Month_Short_StandAlone_6'),
                _pk_translate('Intl_Month_Short_StandAlone_7'),
                _pk_translate('Intl_Month_Short_StandAlone_8'),
                _pk_translate('Intl_Month_Short_StandAlone_9'),
                _pk_translate('Intl_Month_Short_StandAlone_10'),
                _pk_translate('Intl_Month_Short_StandAlone_11'),
                _pk_translate('Intl_Month_Short_StandAlone_12')],
            monthNames: [
                _pk_translate('Intl_Month_Long_StandAlone_1'),
                _pk_translate('Intl_Month_Long_StandAlone_2'),
                _pk_translate('Intl_Month_Long_StandAlone_3'),
                _pk_translate('Intl_Month_Long_StandAlone_4'),
                _pk_translate('Intl_Month_Long_StandAlone_5'),
                _pk_translate('Intl_Month_Long_StandAlone_6'),
                _pk_translate('Intl_Month_Long_StandAlone_7'),
                _pk_translate('Intl_Month_Long_StandAlone_8'),
                _pk_translate('Intl_Month_Long_StandAlone_9'),
                _pk_translate('Intl_Month_Long_StandAlone_10'),
                _pk_translate('Intl_Month_Long_StandAlone_11'),
                _pk_translate('Intl_Month_Long_StandAlone_12')]
        };
    };

    var updateDate;

    function getDatePickerOptions() {
        var result = piwik.getBaseDatePickerOptions(currentDate);
        result.beforeShowDay = isDateInCurrentPeriod;
        result.stepMonths = selectedPeriod == 'year' ? 12 : 1;
        result.onSelect = function () { updateDate.apply(this, arguments); };
        return result;
    }

    $(function () {

        var reloading = false;

        var datepickerElem = $('#datepicker').datepicker(getDatePickerOptions()),
            periodLabels = $('#periodString').find('.period-type label'),
            periodTooltip = $('#periodString').find('.period-click-tooltip').html();

        var toggleWhitespaceHighlighting = function (klass, toggleTop, toggleBottom) {
            var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
                viewedMonth = +$('.ui-datepicker-month', datepickerElem).val(), // convert to int w/ '+'
                firstOfViewedMonth = new Date(viewedYear, viewedMonth, 1),
                lastOfViewedMonth = new Date(viewedYear, viewedMonth + 1, 0);

            // only highlight dates between piwik.minDate... & piwik.maxDate...
            // we select the cells to highlight by checking whether the first & last of the
            // currently viewed month are within the min/max dates.
            if (firstOfViewedMonth >= piwikMinDate) {
                $('tbody>tr:first-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleTop);
            }
            if (lastOfViewedMonth < piwikMaxDate) {
                $('tbody>tr:last-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleBottom);
            }
        };

        // 'this' is the table cell
        var highlightCurrentPeriod = function () {
            switch (selectedPeriod) {
                case 'day':
                    // highlight this link
                    $('a', $(this)).addClass('ui-state-hover');
                    break;
                case 'week':
                    var row = $(this).parent();

                    // highlight parent row (the week)
                    $('a', row).addClass('ui-state-hover');

                    // toggle whitespace if week goes into previous or next month. we check if week is on
                    // top or bottom row.
                    var toggleTop = row.is(':first-child'),
                        toggleBottom = row.is(':last-child');
                    toggleWhitespaceHighlighting('ui-state-hover', toggleTop, toggleBottom);
                    break;
                case 'month':
                    // highlight all parent rows (the month)
                    $('a', $(this).parent().parent()).addClass('ui-state-hover');
                    break;
                case 'year':
                    // highlight table (month + whitespace)
                    $('a', $(this).parent().parent()).addClass('ui-state-hover');
                    toggleWhitespaceHighlighting('ui-state-hover', true, true);
                    break;
            }
        };

        var unhighlightAllDates = function () {
            // make sure nothing is highlighted
            $('.ui-state-active,.ui-state-hover', datepickerElem).removeClass('ui-state-active ui-state-hover');
            $('.ui-datepicker-current-day', datepickerElem).removeClass('ui-datepicker-current-day');

            // color whitespace
            if (piwik.period == 'year') {
                var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
                    toggle = selectedPeriod == 'year' && currentYear == viewedYear;
                toggleWhitespaceHighlighting('ui-datepicker-current-period', toggle, toggle);
            }
            else if (piwik.period == 'week') {
                var toggleTop = $('tr:first-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period'),
                    toggleBottom = $('tr:last-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period');
                toggleWhitespaceHighlighting('ui-datepicker-current-period', toggleTop, toggleBottom);
            }
        };

        updateDate = function (dateText) {
            piwikHelper.showAjaxLoading('ajaxLoadingCalendar');

            // select new dates in calendar
            setCurrentDate(dateText);
            updateDisplayDate(selectedPeriod, dateText);

            // make sure it's called after jquery-ui is done, otherwise everything we do will
            // be undone.
            setTimeout(unhighlightAllDates, 1);

            datepickerElem.datepicker('refresh');

            propagateNewUrlParams(dateText, selectedPeriod);
            initTopControls();
            reloading = false;
        };

        var toggleMonthDropdown = function (disable) {
            if (typeof disable === 'undefined') {
                disable = selectedPeriod == 'year';
            }

            // enable/disable month dropdown based on period == year
            $('.ui-datepicker-month', datepickerElem).attr('disabled', disable);
        };

        var togglePeriodPickers = function (showSingle) {
            $('#periodString').find('.period-date').toggle(showSingle);
            $('#periodString').find('.period-range').toggle(!showSingle);
        };

        //
        // setup datepicker
        //

        unhighlightAllDates();

        //
        // hook up event slots
        //

        // highlight current period when mouse enters date
        datepickerElem.on('mouseenter', 'tbody td', function () {
            if ($(this).hasClass('ui-state-hover')) // if already highlighted, do nothing
            {
                return;
            }

            // unhighlight if cell is disabled/blank, unless the period is year
            if ($(this).hasClass('ui-state-disabled') && selectedPeriod != 'year') {
                unhighlightAllDates();

                // if period is week, then highlight the current week
                if (selectedPeriod == 'week') {
                    highlightCurrentPeriod.call(this);
                }
            }
            else {
                highlightCurrentPeriod.call(this);
            }
        });

        // make sure cell stays highlighted when mouse leaves cell (overrides jquery-ui behavior)
        datepickerElem.on('mouseleave', 'tbody td', function () {
            $('a', this).addClass('ui-state-hover');
        });

        // unhighlight everything when mouse leaves table body (can't do event on tbody, for some reason
        // that fails, so we do two events, one on the table & one on thead)
        datepickerElem.on('mouseleave', 'table', unhighlightAllDates)
            .on('mouseenter', 'thead', unhighlightAllDates);

        // make sure whitespace is clickable when the period makes it appropriate
        datepickerElem.on('click', 'tbody td.ui-datepicker-other-month', function () {
            if ($(this).hasClass('ui-state-hover')) {
                var row = $(this).parent(), tbody = row.parent();

                if (row.is(':first-child')) {
                    // click on first of the month
                    $('a', tbody).first().click();
                }
                else {
                    // click on last of month
                    $('a', tbody).last().click();
                }
            }
        });

        var changePeriodWithPageReload = function (periodInput) {

            var url = periodInput.val(),
                period = broadcast.getValueFromUrl('period', url);

            // if clicking on the selected period, change the period but not the date
            if (period != 'range' && !reloading) {
                // only reload if current period is different from selected
                reloading = true;
                selectedPeriod = period;
                updateDate(piwik.currentDateString);
                return true;
            }

            return false;
        };

        var changePeriodOnClickIfPeriodChanged = function (periodInput) {
            if (reloading) // if a click event resulted in reloading, don't reload again
            {
                return;
            }

            var url = periodInput.val(),
                period = broadcast.getValueFromUrl('period', url);

            // if clicking on the selected period, change the period but not the date
            if (selectedPeriod == period && selectedPeriod != 'range') {
                // only reload if current period is different from selected
                if (piwik.period != selectedPeriod) {
                    return changePeriodWithPageReload(periodInput);
                }

                return true;
            }

            return false;
        };

        $("#otherPeriods").find("label,input").on('dblclick', function (e) {
            var id = $(e.target).attr('for');
            changePeriodOnClickIfPeriodChanged($('#' + id));
        });

        $("#otherPeriods").find("label,input").on('dblclick', function (e) {
            var id = $(e.target).attr('for');
            changePeriodOnClickIfPeriodChanged($('#' + id));
        });

        // Apply date range button will reload the page with the selected range
        $('#calendarApply')
            .on("click", function() {
                var $selectedPeriod = $('#periodMore [name=period]:checked');

                if (!$selectedPeriod.is('#period_id_range')) {
                    changePeriodWithPageReload($selectedPeriod);
                    return true;
                }

                var dateFrom = $('#inputCalendarFrom').val(),
                    dateTo = $('#inputCalendarTo').val(),
                    oDateFrom = $.datepicker.parseDate('yy-mm-dd', dateFrom),
                    oDateTo = $.datepicker.parseDate('yy-mm-dd', dateTo);

                if (!isValidDate(oDateFrom)
                    || !isValidDate(oDateTo)
                    || oDateFrom > oDateTo) {
                    $('#alert').find('h2').text(_pk_translate('General_InvalidDateRange'));
                    piwikHelper.modalConfirm('#alert', {});
                    return false;
                }
                piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
                propagateNewUrlParams(dateFrom + ',' + dateTo, 'range');
            })
            .show();



        // when non-range period is clicked, change the period & refresh the date picker
        $("#otherPeriods").find("input").on('click', function (e) {
            var request_URL = $(e.target).val(),
                period = broadcast.getValueFromUrl('period', request_URL),
                lastPeriod = selectedPeriod;

            if (changePeriodOnClickIfPeriodChanged($(e.target))) {
                return true;
            }

            // switch the selected period
            selectedPeriod = period;

            // remove tooltips from the period inputs
            periodLabels.each(function () { $(this).attr('title', '').removeClass('selected-period-label'); });

            // range periods are handled in an event handler below
            if (period == 'range') {
                return true;
            }

            // set the tooltip of the current period
            if (period != piwik.period) // don't add tooltip for current period
            {
                $(this).parent().find('label[for=period_id_' + period + ']')
                    .attr('title', periodTooltip).addClass('selected-period-label');
            }

            // toggle the right selector controls (show period selector datepicker & hide 'apply range' button)
            togglePeriodPickers(true);

            // set months step to 12 for year period (or set back to 1 if leaving year period)
            if (selectedPeriod == 'year' || lastPeriod == 'year') {
                // setting stepMonths will change the month in view back to the selected date. to avoid
                // we set the selected date to the month in view.
                var currentMonth = $('.ui-datepicker-month', datepickerElem).val(),
                    currentYear = $('.ui-datepicker-year', datepickerElem).val();

                datepickerElem
                    .datepicker('option', 'stepMonths', selectedPeriod == 'year' ? 12 : 1)
                    .datepicker('setDate', new Date(currentYear, currentMonth));
            }

            datepickerElem.datepicker('refresh'); // must be last datepicker call, otherwise cells get highlighted

            unhighlightAllDates();
            toggleMonthDropdown();

            return true;
        });

        // clicking left/right re-enables the month dropdown, so we disable it again
        $(datepickerElem).on('click', '.ui-datepicker-next,.ui-datepicker-prev', function () {
            unhighlightAllDates(); // make sure today's date isn't highlighted & toggle extra year highlighting
            toggleMonthDropdown(selectedPeriod == 'year');
        });

        function onDateRangeSelect(dateText, inst) {
            var toOrFrom = inst.id == 'calendarFrom' ? 'From' : 'To';
            $('#inputCalendar' + toOrFrom).val(dateText);
        }

        // this will trigger to change only the period value on search query and hash string.
        $("#period_id_range").on('click', function (e) {
            togglePeriodPickers(false);

            var options = getDatePickerOptions();

            // Custom Date range callback
            options.onSelect = onDateRangeSelect;
            // Do not highlight the period
            options.beforeShowDay = '';
            // Create both calendars
            options.defaultDate = piwik.startDateString;
            $('#calendarFrom').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.startDateString));

            // Technically we should trigger the onSelect event on the calendar, but I couldn't find how to do that
            // So calling the onSelect bind function manually...
            //$('#calendarFrom').trigger('dateSelected'); // or onSelect
            onDateRangeSelect(piwik.startDateString, { "id": "calendarFrom" });

            // Same code for the other calendar
            options.defaultDate = piwik.endDateString;
            $('#calendarTo').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.endDateString));
            onDateRangeSelect(piwik.endDateString, { "id": "calendarTo" });

            // If not called, the first date appears light brown instead of dark brown
            $('.ui-state-hover').removeClass('ui-state-hover');

            // Bind the input fields to update the calendar's date when date is manually changed
            $('#inputCalendarFrom, #inputCalendarTo')
                .keyup(function (e) {
                    var fromOrTo = this.id == 'inputCalendarFrom' ? 'From' : 'To';
                    var dateInput = $(this).val();
                    try {
                        var newDate = $.datepicker.parseDate('yy-mm-dd', dateInput);
                    } catch (e) {
                        return;
                    }
                    $("#calendar" + fromOrTo).datepicker("setDate", newDate);
                    if (e.keyCode == 13) {
                        $('#calendarApply').click();
                    }
                });
            return true;
        });
        function isValidDate(d) {
            if (Object.prototype.toString.call(d) !== "[object Date]")
                return false;
            return !isNaN(d.getTime());
        }

        if (piwik.period == 'range') {
            $("#period_id_range").click();
        }

        function updatePeriodPickerFromHash()
        {
            var dateHash = broadcast.getValueFromHash('date');
            var periodHash = broadcast.getValueFromHash('period');

            if (!dateHash || dateHash.length > 30 || !periodHash || periodHash.length > 7) {
                // invalid data in URL
                return;
            }

            if (!/^(\d){4}/.test(dateHash)) {
                // it's not an actual date, it is 'yesterday' or so meaning date was not changed in calendar
                return;
            }

            if (piwik.period === periodHash && piwik.currentDateString === dateHash) {
                // this period / date is already loaded
                return;
            }

            $('input[id=period_id_' + periodHash + ']').click();

            updateDate(dateHash);

            $('input[id=period_id_' + periodHash + ']').click();
        }

        if (piwikHelper.isAngularRenderingThePage()) {
            // when using back button etc we need to update the date under circumstances
            angular.element(document).injector().invoke(function ($rootScope) {
                $rootScope.$on('$locationChangeSuccess', updatePeriodPickerFromHash);
            });

            // on initial page load the date/period in hash might be different to the one in the URL
            updatePeriodPickerFromHash();
        }

        initTopControls();
    });

    Mousetrap.bind('d', function(event) {
        if (event.altKey) {
            return;
        }
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false; // IE
        }
        $('#periodString .title').trigger('click').focus();
    });

}(jQuery));
