/* eslint-env jquery, browser */
/* eslint-disable comma-dangle, no-empty */
/* global PARAMS, signal, assert, log, debugPrint */

// ----------------------------------------------------------------------------
// manage jquery-tooltipster hover tooltips
var TooltipManager = (function(global) {
    Object.assign(TooltipManager, {
        BASECONFIG: {
            theme: ['tooltipster-noir'],
            side: ['right','top','bottom', 'left'],
            updateAnimation: 'scale',
            delay: [750, 1000],
            distance: { right: 24, left: 8, top: 8, bottom: 8 },
            contentAsHTML: true,
        },
    });

    function TooltipManager(options) {
        assert(options.elements && options.tooltips, 'TooltipManager constructor expects .elements and .tooltips');
        Object.assign(this, {
            instances: [],
            options: options,
            config: Object.assign({}, TooltipManager.BASECONFIG, {
                trigger: !options.testmode ? 'hover' : 'click',
                interactive: options.testmode,
                minWidth: options.viewport && options.viewport.min.width,
                maxWidth: options.viewport && options.viewport.max.width,
            }),
        });
        options.enabled && this.initialize();
    }

    TooltipManager.prototype = {
        constructor: TooltipManager,
        initialize: function() {
            var options = this.options,
                _config = this.config,
                _self = this,
                candidates = $(options.elements);

            candidates.add($('button')).each(function() {
                var id = this.id,
                    input = $(this),
                    tip = options.tooltips[id] || options.tooltips[input.data('for')];

                var alreadyTipped = input.is('.tooltipstered') || input.closest('.tooltipstered').get(0);
                if (alreadyTipped || !tip) {
                    return !tip && _debugPrint('!tooltippable -- missing tooltip for ' + (id || input.data('for') || input.text()));
                }
                var config = Object.assign({ content: tip }, _config);

                function mergeConfig() {
                    var attr = $(this).attr('data-tooltipster'),
                        object = $(this).data('tooltipster');
                    typeof object === 'object' && Object.assign(config, object);
                    attr && Object.assign(config, JSON.parse(attr));
                }
                try {
                    input.parents(':data(tooltipster),[data-tooltipster]').each(mergeConfig);
                    input.each(mergeConfig); // prioritize own settings
                } catch(e) {
                    console.error('error extracting tooltipster data:' + [e, id]);
                }

                var target = $(input.closest('.tooltip-target').get(0) ||
                               (input.is('input') && input) || null);

                assert(target && target[0] && tip);
                debugPrint('binding tooltip', config, target[0].nodeName, id || target[0]);
                var instance = target.tooltipster(config)
                    .tooltipster('instance');

                instance.on('close', function(event) {
                    if (options.keepopen === target) {
                        debugPrint(event.type, 'canceling close keepopen === target', id);
                        event.stop();
                        options.keepopen = null;
                    }
                });
                instance.on('before', function(event) {
                    debugPrint(event.type, 'before', event);
                    !options.testmode && _self.closeAll();
                    !options.enabled && event.stop();
                });
                target.find(':focusable, input, [tabindex], button, .control')
                    .add(target).add(input)
                    .add(input.closest(':focusable, input, [tabindex]'))
                    .on({
                        click: function(evt) {
                            if (input.is('button')) {
                                return setTimeout(instance.close.bind(instance,null),50);
                            }
                            options.keepopen = target;
                        },
                        focus: instance.open.bind(instance, null),
                        blur: function(evt) {
                            instance.close(); _self.openFocusedTooltip();
                        },
                    });
                _self.instances.push(instance);
            });
            return this.instances;
        },
        openFocusedTooltip: function() {
            if (!this.options.enabled) {
                return;
            }
            setTimeout(function() {
                if (!document.activeElement || document.activeElement === document.body ||
                    !$(document.activeElement).closest('section')) {
                    return;
                }
                var tip = $([])
                    .add($(document.activeElement))
                    .add($(document.activeElement).find('.tooltipstered'))
                    .add($(document.activeElement).closest('.tooltipstered'))
                    .filter('.tooltipstered');
                if (tip.is('.tooltipstered')) {
                    // log('opening focused tooltip', tip.length, tip[0].id);
                    tip.tooltipster('open');
                }
            }, 1);
        },
        rapidClose: function(instance, reopen) {
            if (!instance.status().open) {
                return;
            }
            instance.elementTooltip() && $(instance.elementTooltip()).hide();
            instance.close(function() {
                reopen && instance.open();
            });
        },
        openAll: function() {
            $('.tooltipstered').tooltipster('open');
        },
        closeAll: function() {
            $.tooltipster.instances().forEach(function(instance) {
                this.rapidClose(instance);
            }.bind(this));
        },
        updateViewport: function(viewport) {
            var options = {
                minWidth: viewport.min.width,
                maxWidth: viewport.max.width,
            };
            Object.assign(this.config, options);
            $.tooltipster.setDefaults(options);
            debugPrint('updating tooltipster options', JSON.stringify(options));
            $.tooltipster.instances().forEach(function(instance) {
                instance.option('minWidth', options.minWidth);
                instance.option('maxWidth', options.maxWidth);
                this.rapidClose(instance, instance.status().open);
            }.bind(this));
        },
        enable: function() {
            this.options.enabled = true;
            if (this.options.testmode) {
                this.openAll();
            }
        },
        disable: function() {
            this.options.enabled = false;
            this.closeAll();
        },
    };// prototype

    return TooltipManager;
})(this);