/**
 * data-range-selector.src.js
 *
 * @author Daniele De Nobili
 */

/*jslint nomen: true, plusplus: true, passfail: true, browser: true, devel: true */
/*global $, gapi */

/**
 * A DateRangeSelector component for the Embed API.
 */
gapi.analytics.ready(function () {

    "use strict";

    var nDaysAgo = /(\d+)daysAgo/,
        dateFormat = /\d{4}\-\d{2}\-\d{2}/;

    /**
     * Accept a number and return a date formatted as YYYY-MM-DD that
     * represents that many days ago.
     * @return {string} The formatted date.
     */
    function daysAgo(numDays) {
        var date, month, day;

        date = new Date();
        date.setDate(date.getDate() - numDays);
        month = String(date.getMonth() + 1);
        month = month.length === 1 ? '0' + month : month;
        day = String(date.getDate());
        day = day.length === 1 ? '0' + day : day;

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

    /**
     * Convert a date acceptable to the Core Reporting API (e.g. `today`,
     * `yesterday` or `NdaysAgo`) into the format YYYY-MM-DD. Dates
     * already in that format are simply returned.
     * @return {string} The formatted date.
     */
    function convertDate(str) {
        // If str is in the proper format, do nothing.
        if (dateFormat.test(str)) {
            return str;
        }

        var match = nDaysAgo.exec(str);

        if (match) {
            return daysAgo(+match[1]);
        }

        if (str === 'today') {
            return daysAgo(0);
        }

        if (str === 'yesterday') {
            return daysAgo(1);
        }

        throw new Error('Cannot convert date ' + str);
    }

    gapi.analytics.createComponent('DateRangeSelector', {

        /**
         * Initialize the DateRangeSelector instance and render it to the page.
         * @return {DateRangeSelector} The instance.
         */
        execute: function () {
            var options, dateInputs;

            options = this.get();
            options['start-date'] = options['start-date'] || '7daysAgo';
            options['end-date'] = options['end-date'] || 'yesterday';

            // Allow container to be a string ID or an HTMLElement.
            this.container = typeof options.container === 'string' ? document.getElementById(options.container) : options.container;

            // Allow the template to be overridden.
            if (options.template) {
                this.template = options.template;
            }

            this.container.innerHTML = this.template;
            dateInputs = this.container.querySelectorAll('input');

            this.startDateInput = dateInputs[0];
            this.startDateInput.value = convertDate(options['start-date']);
            this.endDateInput = dateInputs[1];
            this.endDateInput.value = convertDate(options['end-date']);

            this._applyDatePicker();
            this.setValues();
            this.setMinMax();

            this.container.onchange = this.onChange.bind(this);

            return this;
        },

        /**
         * Emit a change event based on the currently selected dates.
         * Pass an object containing the start date and end date.
         */
        onChange: function () {
            this.setValues();
            this.setMinMax();
            this.emit('change', {
                'start-date': this['start-date'],
                'end-date': this['end-date']
            });
        },

        _applyDatePicker: function () {
            var opts = {
                dateFormat: 'yy-mm-dd',
                changeMonth: true,
                changeYear: true,
                showOtherMonths: true,
                selectOtherMonths: true
            };

            this.$startDateInput = $(this.startDateInput);
            this.$endDateInput = $(this.endDateInput);

            this.$startDateInput.datepicker(opts);
            this.$endDateInput.datepicker(opts);
        },

        /**
         * Updates the instance properties based on the input values.
         */
        setValues: function () {
            this['start-date'] = this.startDateInput.value;
            this['end-date'] = this.endDateInput.value;
        },

        /**
         * Updates the input min and max attributes so there's no overlap.
         */
        setMinMax: function () {
            //this.startDateInput.max = this.endDateInput.value;
            //this.endDateInput.min = this.startDateInput.value;
            this.$startDateInput.datepicker('option', 'maxDate', this.endDateInput.value);
            this.$endDateInput.datepicker('option', 'minDate', this.startDateInput.value);
        },

        /**
         * The html structure used to build the component. Developers can
         * override this by passing it to the component constructor.
         * The only requirement is that the structure contain two inputs, the
         * first will be the start date and the second will be the end date.
         */
        template: '<div class="date-range-selector">' +
            '<div class="date-range-selector--item">' +
            '   <label>Start Date</label><input type="text">' +
            '</div>' +
            '<div class="date-range-selector--item">' +
            '   <label>End Date</label><input type="text">' +
            '</div>' +
            '</div>'
    });

});
