/*
 * DatePicker plugin
 *
 * - Documentation: ../docs/datepicker.md
 *
 * Dependences:
 * - Pikaday plugin (pikaday.js)
 * - Pikaday jQuery addon (pikaday.jquery.js)
 * - Clockpicker plugin (jquery-clockpicker.js)
 * - Moment library (moment.js)
 * - Moment Timezone library (moment.timezone.js)
 */

+function ($) { "use strict";

    var Base = $.wn.foundation.base,
        BaseProto = Base.prototype

    var DatePicker = function (element, options) {
        this.$el = $(element)
        this.options = options || {}

        $.wn.foundation.controlUtils.markDisposable(element)
        Base.call(this)
        this.init()
    }

    DatePicker.prototype = Object.create(BaseProto)
    DatePicker.prototype.constructor = DatePicker

    DatePicker.prototype.init = function() {
        var self = this,
            $form = this.$el.closest('form'),
            changeMonitor = $form.data('oc.changeMonitor')

        if (changeMonitor !== undefined) {
            changeMonitor.pause()
        }

        this.dbDateTimeFormat = 'YYYY-MM-DD HH:mm:ss'
        this.dbDateFormat = 'YYYY-MM-DD'
        this.dbTimeFormat = 'HH:mm:ss'

        this.$dataLocker = $('[data-datetime-value]', this.$el)
        this.$datePicker = $('[data-datepicker]', this.$el)
        this.$timePicker = $('[data-timepicker]', this.$el)
        this.hasDate = !!this.$datePicker.length
        this.hasTime = !!this.$timePicker.length
        this.ignoreTimezone = this.$el.get(0).hasAttribute('data-ignore-timezone')

        this.initRegion()

        if (this.hasDate) {
            this.initDatePicker()
        }

        if (this.hasTime) {
            this.initTimePicker()
        }

        if (changeMonitor !== undefined) {
            changeMonitor.resume()
        }

        this.$timePicker.on('change.oc.datepicker', function() {
            if (!$.trim($(this).val())) {
                self.emptyValues()
            }
            else {
                self.onSelectTimePicker()
            }
        })

        this.$datePicker.on('change.oc.datepicker', function() {
            if (!$.trim($(this).val())) {
                self.emptyValues()
            }
        })

        this.$el.one('dispose-control', this.proxy(this.dispose))
    }

    DatePicker.prototype.dispose = function() {
        this.$timePicker.off('change.oc.datepicker')
        this.$datePicker.off('change.oc.datepicker')
        this.$el.off('dispose-control', this.proxy(this.dispose))
        this.$el.removeData('oc.datePicker')

        this.$el = null
        this.options = null

        BaseProto.dispose.call(this)
    }

    //
    // Datepicker
    //

    DatePicker.prototype.initDatePicker = function() {
        var self = this,
            dateFormat = this.getDateFormat(),
            now = moment().tz(this.timezone).format(dateFormat)

        var pikadayOptions = {
            yearRange: this.options.yearRange,
            firstDay: this.options.firstDay,
            showWeekNumber: this.options.showWeekNumber,
            format: dateFormat,
            setDefaultDate: now,
            onOpen: function() {
                var $field = $(this._o.trigger)

                $(this.el).css({
                    left: 'auto',
                    right: $(window).width() - $field.offset().left - $field.outerWidth()
                })
            },
            onSelect: function() {
                self.onSelectDatePicker.call(self, this.getMoment())
            }
        }

        var lang = this.getLang('datepicker', false)
        if (lang) {
            pikadayOptions.i18n = lang
        }

        this.$datePicker.val(this.getDataLockerValue(dateFormat))

        if (this.options.minDate) {
            pikadayOptions.minDate = new Date(this.options.minDate)
        }

        if (this.options.maxDate) {
            pikadayOptions.maxDate = new Date(this.options.maxDate)
        }

        this.$datePicker.pikaday(pikadayOptions)

        // Avoid displaying keyboards on mobile when the widget is displayed
        if (!this.$datePicker.attr('inputmode')) {
            this.$datePicker.attr('inputmode', 'none')
        }
    }

    DatePicker.prototype.onSelectDatePicker = function(pickerMoment) {
        var pickerValue = pickerMoment.format(this.dbDateFormat)

        var timeValue = this.options.mode === 'date' ? '00:00:00' : this.getTimePickerValue()

        var momentObj = moment
            .tz(pickerValue + ' ' + timeValue, this.dbDateTimeFormat, this.timezone)
            .tz(this.appTimezone)

        var lockerValue = momentObj.format(this.dbDateTimeFormat)

        this.$dataLocker.val(lockerValue)
    }

    // Returns in user preference timezone
    DatePicker.prototype.getDatePickerValue = function() {
        var value = this.$datePicker.val()

        if (!this.hasDate || !value) {
            return moment.tz(this.appTimezone)
                .tz(this.timezone)
                .format(this.dbDateFormat)
        }

        return moment(value, this.getDateFormat()).format(this.dbDateFormat)
    }

    DatePicker.prototype.getDateFormat = function() {
        var format = 'YYYY-MM-DD'

        if (this.options.format) {
            format = this.options.format
        }
        else if (this.locale) {
            format = moment()
                .locale(this.locale)
                .localeData()
                .longDateFormat('l')
        }

        return format
    }

    //
    // Timepicker
    //

    DatePicker.prototype.initTimePicker = function() {
        this.$timePicker.clockpicker({
            autoclose: 'true',
            placement: 'auto',
            align: 'right',
            twelvehour: this.isTimeTwelveHour(),
            afterDone: this.proxy(this.onChangeTimePicker)
        })

        this.$timePicker.val(this.getDataLockerValue(this.getTimeFormat()))

        // Avoid displaying keyboards on mobile when the widget is displayed
        if (!this.$timePicker.attr('inputmode')) {
            this.$timePicker.attr('inputmode', 'none')
        }
    }

    DatePicker.prototype.onSelectTimePicker = function() {
        var pickerValue = this.$timePicker.val()

        var timeValue = moment(pickerValue, this.getTimeFormat()).format(this.dbTimeFormat)

        var dateValue = this.getDatePickerValue()

        var momentObj = moment
            .tz(dateValue + ' ' + timeValue, this.dbDateTimeFormat, this.timezone)
            .tz(this.appTimezone)

        var lockerValue = momentObj.format(this.dbDateTimeFormat)

        this.$dataLocker.val(lockerValue)
    }

    DatePicker.prototype.onChangeTimePicker = function() {
        // Trigger a change event when the time is changed, to allow dependent fields to refresh
        this.$timePicker.trigger('change')
    }

    // Returns in user preference timezone
    DatePicker.prototype.getTimePickerValue = function() {
        var value = this.$timePicker.val()

        if (!this.hasTime || !value) {
            return moment.tz(this.appTimezone)
                .tz(this.timezone)
                .format(this.dbTimeFormat)
        }

        return moment(value, this.getTimeFormat()).format(this.dbTimeFormat)
    }

    DatePicker.prototype.getTimeFormat = function() {
        return this.isTimeTwelveHour()
            ? 'hh:mm A'
            : 'HH:mm'
    }

    DatePicker.prototype.isTimeTwelveHour = function() {
        return false

        // Disabled for now: The analog clock design is pretty good
        // at representing time regardless of the format. If we want
        // to enable this, there should be some way to disable it
        // again via the form field options.

        // var momentObj = moment()

        // if (this.locale) {
        //     momentObj = momentObj.locale(this.locale)
        // }

        // return momentObj
        //     .localeData()
        //     .longDateFormat('LT')
        //     .indexOf('A') !== -1;
    }

    //
    // Utilities
    //

    DatePicker.prototype.emptyValues = function() {
        this.$dataLocker.val('')
        this.$datePicker.val('')
        this.$timePicker.val('')
    }

    DatePicker.prototype.getDataLockerValue = function(format) {
        var value = this.$dataLocker.val()

        return value
            ? this.getMomentLoadValue(value, format)
            : null
    }

    DatePicker.prototype.getMomentLoadValue = function(value, format) {
        var momentObj = moment.tz(value, this.appTimezone)

        if (this.locale) {
            momentObj = momentObj.locale(this.locale)
        }

        momentObj = momentObj.tz(this.timezone)

        return momentObj.format(format)
    }

    DatePicker.prototype.initRegion = function() {
        this.locale = $('meta[name="backend-locale"]').attr('content')
        this.timezone = $('meta[name="backend-timezone"]').attr('content')
        this.appTimezone = $('meta[name="app-timezone"]').attr('content')

        if (!this.appTimezone) {
            this.appTimezone = 'UTC'
        }

        if (!this.timezone) {
            this.timezone = 'UTC'
        }

        // Set both timezones to UTC to disable converting between them
        if (this.ignoreTimezone) {
            this.appTimezone = 'UTC'
            this.timezone = 'UTC'
        }
    }

    DatePicker.prototype.getLang = function(name, defaultValue) {
        if ($.oc === undefined || $.wn.lang === undefined) {
            return defaultValue
        }

        return $.wn.lang.get(name, defaultValue)
    }

    DatePicker.DEFAULTS = {
        minDate: null,
        maxDate: null,
        format: null,
        yearRange: 10,
        firstDay: 0,
        showWeekNumber: false,
        mode: 'datetime'
    }

    // PLUGIN DEFINITION
    // ============================

    var old = $.fn.datePicker

    $.fn.datePicker = function (option) {
        var args = Array.prototype.slice.call(arguments, 1), items, result

        items = this.each(function () {
            var $this   = $(this)
            var data    = $this.data('oc.datePicker')
            var options = $.extend({}, DatePicker.DEFAULTS, $this.data(), typeof option == 'object' && option)
            if (!data) $this.data('oc.datePicker', (data = new DatePicker(this, options)))
            if (typeof option == 'string') result = data[option].apply(data, args)
            if (typeof result != 'undefined') return false
        })

        return result ? result : items
    }

    $.fn.datePicker.Constructor = DatePicker

    $.fn.datePicker.noConflict = function () {
        $.fn.datePicker = old
        return this
    }

    $(document).on('render', function() {
        $('[data-control="datepicker"]').datePicker()
    });

}(window.jQuery);
