/*!
 * Piwik - Web Analytics
 *
 * @link http://piwik.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */

(function ($, piwik) {

    var annotationsApi = {

        // calls Annotations.getAnnotationManager
        getAnnotationManager: function (idSite, date, period, lastN, callback) {
            var ajaxParams =
            {
                module: 'Annotations',
                action: 'getAnnotationManager',
                idSite: idSite,
                date: date,
                period: period
            };
            if (lastN) {
                ajaxParams.lastN = lastN;
            }

            var ajaxRequest = new ajaxHelper();
            ajaxRequest.addParams(ajaxParams, 'get');
            ajaxRequest.setCallback(callback);
            ajaxRequest.setFormat('html');
            ajaxRequest.send(false);
        },

        // calls Annotations.addAnnotation
        addAnnotation: function (idSite, managerDate, managerPeriod, date, note, callback) {
            var ajaxParams =
            {
                module: 'Annotations',
                action: 'addAnnotation',
                idSite: idSite,
                date: date,
                managerDate: managerDate,
                managerPeriod: managerPeriod,
                note: note
            };

            var ajaxRequest = new ajaxHelper();
            ajaxRequest.addParams(ajaxParams, 'get');
            ajaxRequest.setCallback(callback);
            ajaxRequest.setFormat('html');
            ajaxRequest.send(false);
        },

        // calls Annotations.saveAnnotation
        saveAnnotation: function (idSite, idNote, date, noteData, callback) {
            var ajaxParams =
            {
                module: 'Annotations',
                action: 'saveAnnotation',
                idSite: idSite,
                idNote: idNote,
                date: date
            };

            for (var key in noteData) {
                ajaxParams[key] = noteData[key];
            }

            var ajaxRequest = new ajaxHelper();
            ajaxRequest.addParams(ajaxParams, 'get');
            ajaxRequest.setCallback(callback);
            ajaxRequest.setFormat('html');
            ajaxRequest.send(false);
        },

        // calls Annotations.deleteAnnotation
        deleteAnnotation: function (idSite, idNote, managerDate, managerPeriod, callback) {
            var ajaxParams =
            {
                module: 'Annotations',
                action: 'deleteAnnotation',
                idSite: idSite,
                idNote: idNote,
                date: managerDate,
                period: managerPeriod
            };

            var ajaxRequest = new ajaxHelper();
            ajaxRequest.addParams(ajaxParams, 'get');
            ajaxRequest.setCallback(callback);
            ajaxRequest.setFormat('html');
            ajaxRequest.send(false);
        },

        // calls Annotations.getEvolutionIcons
        getEvolutionIcons: function (idSite, date, period, lastN, callback) {
            var ajaxParams =
            {
                module: 'Annotations',
                action: 'getEvolutionIcons',
                idSite: idSite,
                date: date,
                period: period
            };
            if (lastN) {
                ajaxParams.lastN = lastN;
            }

            var ajaxRequest = new ajaxHelper();
            ajaxRequest.addParams(ajaxParams, 'get');
            ajaxRequest.setFormat('html');
            ajaxRequest.setCallback(callback);
            ajaxRequest.send(false);
        }
    };

    var today = new Date();

    /**
     * Returns options to configure an annotation's datepicker shown in edit mode.
     *
     * @param {Element} annotation The annotation element.
     */
    var getDatePickerOptions = function (annotation) {
        var annotationDateStr = annotation.attr('data-date'),
            parts = annotationDateStr.split('-'),
            annotationDate = new Date(parts[0], parts[1] - 1, parts[2]);

        var result = piwik.getBaseDatePickerOptions(annotationDate);

        // make sure days before site start & after today cannot be selected
        var piwikMinDate = result.minDate;
        result.beforeShowDay = function (date) {
            var valid = true;

            // if date is after today or before date of site creation, it cannot be selected
            if (date > today
                || date < piwikMinDate) {
                valid = false;
            }

            return [valid, ''];
        };

        // on select a date, change the text of the edit date link
        result.onSelect = function (dateText) {
            $('.annotation-period-edit>a', annotation).text(dateText);
            $('.datepicker', annotation).hide();
        };

        return result;
    };

    /**
     * Switches the current mode of an annotation between the view/edit modes.
     *
     * @param {Element} inAnnotationElement An element within the annotation to toggle the mode of.
     *                                      Should be two levels nested in the .annotation-value
     *                                      element.
     * @return {Element} The .annotation-value element.
     */
    var toggleAnnotationMode = function (inAnnotationElement) {
        var annotation = $(inAnnotationElement).closest('.annotation');
        $('.annotation-period,.annotation-period-edit,.delete-annotation,' +
            '.annotation-edit-mode,.annotation-view-mode', annotation).toggle();

        return $(inAnnotationElement).find('.annotation-value');
    };

    /**
     * Creates the datepicker for an annotation element.
     *
     * @param {Element} annotation The annotation element.
     */
    var createDatePicker = function (annotation) {
        $('.datepicker', annotation).datepicker(getDatePickerOptions(annotation)).hide();
    };

    /**
     * Creates datepickers for every period edit in an annotation manager.
     *
     * @param {Element} manager The annotation manager element.
     */
    var createDatePickers = function (manager) {
        $('.annotation-period-edit', manager).each(function () {
            createDatePicker($(this).parent().parent());
        });
    }

    /**
     * Replaces the HTML of an annotation manager element, and resets date/period
     * attributes.
     *
     * @param {Element} manager The annotation manager.
     * @param {string} html The HTML of the new annotation manager.
     */
    var replaceAnnotationManager = function (manager, html) {
        var newManager = $(html);
        manager.html(newManager.html())
            .attr('data-date', newManager.attr('data-date'))
            .attr('data-period', newManager.attr('data-period'));
        createDatePickers(manager);
    };

    /**
     * Returns true if an annotation element is starred, false if otherwise.
     *
     * @param {Element} annotation The annotation element.
     * @return {boolean}
     */
    var isAnnotationStarred = function (annotation) {
        return !!(+$('.annotation-star', annotation).attr('data-starred') == 1);
    };

    /**
     * Replaces the HTML of an annotation element with HTML returned from Piwik, and
     * makes sure the data attributes are correct.
     *
     * @param {Element} annotation The annotation element.
     * @param {string} html The replacement HTML (or alternatively, the replacement
     *                      element/jQuery object).
     */
    var replaceAnnotationHtml = function (annotation, html) {
        var newHtml = $(html);
        annotation.html(newHtml.html()).attr('data-date', newHtml.attr('data-date'));
        createDatePicker(annotation);
    }

    /**
     * Binds events to an annotation manager element.
     *
     * @param {Element} manager The annotation manager.
     * @param {int} idSite The site ID the manager is showing annotations for.
     * @param {function} onAnnotationCountChange Callback that is called when there is a change
     *                                           in the number of annotations and/or starred annotations,
     *                                           eg, when a user adds a new one or deletes an existing one.
     */
    var bindAnnotationManagerEvents = function (manager, idSite, onAnnotationCountChange) {
        if (!onAnnotationCountChange) {
            onAnnotationCountChange = function () {};
        }

        // show new annotation row if create new annotation link is clicked
        manager.on('click', '.add-annotation', function (e) {
            e.preventDefault();

            $('.new-annotation-row', manager).show();
            $(this).hide();

            return false;
        });

        // hide new annotation row if cancel button clicked
        manager.on('click', '.new-annotation-cancel', function () {
            var newAnnotationRow = $(this).parent().parent();
            newAnnotationRow.hide();

            $('.add-annotation', newAnnotationRow.closest('.annotation-manager')).show();
        });

        // save new annotation when new annotation row save is clicked
        manager.on('click', '.new-annotation-save', function () {
            var addRow = $(this).parent().parent(),
                addNoteInput = addRow.find('.new-annotation-edit'),
                noteDate = addRow.find('.annotation-period-edit>a').text();

            // do nothing if input is empty
            if (!addNoteInput.val()) {
                return;
            }

            // disable input & link
            addNoteInput.attr('disabled', 'disabled');
            $(this).attr('disabled', 'disabled');

            // add a new annotation for the site, date & period
            annotationsApi.addAnnotation(
                idSite,
                manager.attr('data-date'),
                manager.attr('data-period'),
                noteDate,
                addNoteInput.val(),
                function (response) {
                    replaceAnnotationManager(manager, response);

                    // increment annotation count for this date
                    onAnnotationCountChange(noteDate, 1, 0);
                }
            );
        });

        // add new annotation when enter key pressed on new annotation input
        manager.on('keypress', '.new-annotation-edit', function (e) {
            if (e.which == 13) {
                $(this).parent().find('.new-annotation-save').click();
            }
        });

        // show annotation editor if edit link, annotation text or period text is clicked
        manager.on('click', '.annotation-enter-edit-mode', function (e) {
            e.preventDefault();

            var annotationContent = toggleAnnotationMode(this);
            annotationContent.find('.annotation-edit').focus();

            return false;
        });

        // hide annotation editor if cancel button is clicked
        manager.on('click', '.annotation-cancel', function () {
            toggleAnnotationMode(this);
        });

        // save annotation if save button clicked
        manager.on('click', '.annotation-edit-mode .annotation-save', function () {
            var annotation = $(this).parent().parent().parent(),
                input = $('.annotation-edit', annotation),
                dateEditText = $('.annotation-period-edit>a', annotation).text();

            // if annotation value/date has not changed, just show the view mode instead of edit
            if (input[0].defaultValue == input.val()
                && dateEditText == annotation.attr('data-date')) {
                toggleAnnotationMode(this);
                return;
            }

            // disable input while ajax is happening
            input.attr('disabled', 'disabled');
            $(this).attr('disabled', 'disabled');

            // save the note w/ the new note text & date
            annotationsApi.saveAnnotation(
                idSite,
                annotation.attr('data-id'),
                dateEditText,
                {
                    note: input.val()
                },
                function (response) {
                    response = $(response);

                    var newDate = response.attr('data-date'),
                        isStarred = isAnnotationStarred(response),
                        originalDate = annotation.attr('data-date');

                    replaceAnnotationHtml(annotation, response);

                    // if the date has been changed, update the evolution icon counts to reflect the change
                    if (originalDate != newDate) {
                        // reduce count for original date
                        onAnnotationCountChange(originalDate, -1, isStarred ? -1 : 0);

                        // increase count for new date
                        onAnnotationCountChange(newDate, 1, isStarred ? 1 : 0);
                    }
                }
            );
        });

        // save annotation if 'enter' pressed on input
        manager.on('keypress', '.annotation-value input', function (e) {
            if (e.which == 13) {
                $(this).parent().find('.annotation-save').click();
            }
        });

        // delete annotation if delete link clicked
        manager.on('click', '.delete-annotation', function (e) {
            e.preventDefault();

            var annotation = $(this).parent().parent();
            $(this).attr('disabled', 'disabled');

            // delete annotation by ajax
            annotationsApi.deleteAnnotation(
                idSite,
                annotation.attr('data-id'),
                manager.attr('data-date'),
                manager.attr('data-period'),
                function (response) {
                    replaceAnnotationManager(manager, response);

                    // update evolution icons
                    var isStarred = isAnnotationStarred(annotation);
                    onAnnotationCountChange(annotation.attr('data-date'), -1, isStarred ? -1 : 0);
                }
            );

            return false;
        });

        // star/unstar annotation if star clicked
        manager.on('click', '.annotation-star-changeable', function (e) {
            var annotation = $(this).parent().parent(),
                newStarredVal = $(this).attr('data-starred') == 0 ? 1 : 0 // flip existing 'starred' value
                ;

            // perform ajax request to star annotation
            annotationsApi.saveAnnotation(
                idSite,
                annotation.attr('data-id'),
                annotation.attr('data-date'),
                {
                    starred: newStarredVal
                },
                function (response) {
                    replaceAnnotationHtml(annotation, response);

                    // change starred count for this annotation in evolution graph based on what we're
                    // changing the starred value to
                    onAnnotationCountChange(annotation.attr('data-date'), 0, newStarredVal == 0 ? -1 : 1);
                }
            );
        });

        // when period edit is clicked, show datepicker
        manager.on('click', '.annotation-period-edit>a', function (e) {
            e.preventDefault();
            $('.datepicker', $(this).parent()).toggle();
            return false;
        });

        // make sure datepicker popups are closed if someone clicks elsewhere
        $('body').on('mouseup', function (e) {
            var container = $('.annotation-period-edit>.datepicker:visible').parent();

            if (!container.has(e.target).length) {
                container.find('.datepicker').hide();
            }
        });
    };

// used in below function
    var loadingAnnotationManager = false;

    /**
     * Shows an annotation manager under a report for a specific site & date range.
     *
     * @param {Element} domElem The element of the report to show the annotation manger
     *                          under.
     * @param {int} idSite The ID of the site to show the annotations of.
     * @param {string} date The start date of the period.
     * @param {string} period The period type.
     * @param {int} lastN Whether to include the last N periods in the date range or not. Can
     *              be undefined.
     */
    var showAnnotationViewer = function (domElem, idSite, date, period, lastN, callback) {
        var addToAnnotationCount = function (date, amt, starAmt) {
            if (date.indexOf(',') != -1) {
                date = date.split(',')[0];
            }

            $('.evolution-annotations>span', domElem).each(function () {
                if ($(this).attr('data-date') == date) {
                    // get counts from attributes (and convert them to ints)
                    var starredCount = +$(this).attr('data-starred'),
                        annotationCount = +$(this).attr('data-count');

                    // modify the starred count & make sure the correct image is used
                    var newStarCount = starredCount + starAmt;
                    if (newStarCount > 0) {
                        var newImg = 'themes/default/images/yellow_marker.png';
                    } else {
                        var newImg = 'themes/default/images/grey_marker.png';
                    }
                    $(this).attr('data-starred', newStarCount).find('img').attr('src', newImg);

                    // modify the annotation count & hide/show based on new count
                    var newCount = annotationCount + amt;
                    $(this).attr('data-count', newCount).css('opacity', newCount > 0 ? 1 : 0);

                    return false;
                }
            });
        };

        var manager = $('.annotation-manager', domElem);
        if (manager.length) {
            // if annotations for the requested date + period are already loaded, then just toggle the
            // visibility of the annotation viewer. otherwise, we reload the annotations.
            if (manager.attr('data-date') == date
                && manager.attr('data-period') == period) {
                // toggle manager view
                if (manager.is(':hidden')) {
                    manager.slideDown('slow', function () { if (callback) callback(manager) });
                }
                else {
                    manager.slideUp('slow', function () { if (callback) callback(manager) });
                }
            }
            else {
                // show nothing but the loading gif
                $('.annotations', manager).html('');
                $('.loadingPiwik', manager).show();

                // reload annotation manager for new date/period
                annotationsApi.getAnnotationManager(idSite, date, period, lastN, function (response) {
                    replaceAnnotationManager(manager, response);

                    createDatePickers(manager);

                    // show if hidden
                    if (manager.is(':hidden')) {
                        manager.slideDown('slow', function () { if (callback) callback(manager) });
                    }
                    else {
                        if (callback) {
                            callback(manager);
                        }
                    }
                });
            }
        }
        else {
            // if we are already loading the annotation manager, don't load it again
            if (loadingAnnotationManager) {
                return;
            }

            loadingAnnotationManager = true;

            var loading = $('.loadingPiwikBelow', domElem).css({display: 'block'});

            // the annotations for this report have not been retrieved yet, so do an ajax request
            // & show the result
            annotationsApi.getAnnotationManager(idSite, date, period, lastN, function (response) {
                var manager = $(response).hide();

                // if an error occurred (and response does not contain the annotation manager), do nothing
                if (!manager.hasClass('annotation-manager')) {
                    return;
                }

                // create datepickers for each shown annotation
                createDatePickers(manager);

                bindAnnotationManagerEvents(manager, idSite, addToAnnotationCount);

                loading.css('visibility', 'hidden');

                // add & show annotation manager
                $('.dataTableFeatures', domElem).append(manager);
                manager.slideDown('slow', function () {
                    loading.hide().css('visibility', 'visible');
                    loadingAnnotationManager = false;

                    if (callback) callback(manager)
                });
            });
        }
    };

    /**
     * Determines the x-coordinates of a set of evolution annotation icons.
     *
     * @param {Element} annotations The '.evolution-annotations' element.
     * @param {Element} graphElem The evolution graph's datatable element.
     */
    var placeEvolutionIcons = function (annotations, graphElem) {
        var canvases = $('.piwik-graph .jqplot-xaxis canvas', graphElem),
            noteSize = 16;

        // if no graph available, hide all icons
        if (!canvases || canvases.length == 0) {
            $('span', annotations).hide();
            return true;
        }

        // set position of each individual icon
        $('span', annotations).each(function (i) {
            var canvas = $(canvases[i]),
                canvasCenterX = canvas.position().left + (canvas.width() / 2);
            $(this).css({
                left: canvasCenterX - noteSize / 2,
                // show if there are annotations for this x-axis tick
                opacity: +$(this).attr('data-count') > 0 ? 1 : 0
            });
        });
    };

// make showAnnotationViewer, placeEvolutionIcons & annotationsApi globally accessible
    piwik.annotations = {
        showAnnotationViewer: showAnnotationViewer,
        placeEvolutionIcons: placeEvolutionIcons,
        api: annotationsApi
    };

}(jQuery, piwik));
