// Pure JavaScript implementation of jQuery-menu-aim from kamens
// https://github.com/kamens/jQuery-menu-aim/blob/master/jQuery-menu-aim.js

class MenuAim {
    constructor(menu, rowSelector, activeClass, tolerance) {
        this.menu = menu;
        this.rowSelector = rowSelector;
        this.activeClass = activeClass;
        this.tolerance = tolerance || 75;


        this.lastRow = null;
        this.mouseLocs = [];
        this.timeoutId = null;
        this.options = {
            tolerance: this.tolerance,
            enter: function() {},
            exit: function() {},
            activate: function() {},
            deactivate: function() {},
            exitMenu: function() {}
        };

        this.mousemoveDocument = function(e) {
            this.mouseLocs.push({x: e.pageX, y: e.pageY});
            if (this.mouseLocs.length > this.options.tolerance) {
                this.mouseLocs.shift();
            }
        };

        this.mouseleaveMenu = function() {
            if (this.timeoutId) {
                clearTimeout(this.timeoutId);
            }
            this.options.exitMenu(this.menu);
            if (this.lastRow) {
                this.options.deactivate(this.lastRow);
            }
            this.lastRow = null;
        };

        this.mouseenterRow = function() {
            if (this.timeoutId) {
                clearTimeout(this.timeoutId);
            }
            this.options.enter(this);
            this.possiblyActivate(this);
        };

        this.mouseleaveRow = function() {
            this.options.exit(this);
        };

        this.clickRow = function() {
            this.activate(this);
        };

        this.activate = function(row) {
            if (row == this.lastRow) {
                return;
            }
            if (this.lastRow) {
                this.options.deactivate(this.lastRow);
            }
            this.options.activate(row);
            this.lastRow = row;
        };

        this.possiblyActivate = function(row) {
            let delay = this.activationDelay();
            if (delay) {
                this.timeoutId = setTimeout(function() {
                    this.possiblyActivate(row);
                }, delay);
            } else {
                this.activate(row);
            }
        };

        this.activationDelay = function() {
            if (!this.lastRow) {
                return 0;
            }
            let upperRow = this.lastRow;
            let lowerRow = row;
            let loc = this.mouseLocs[this.mouseLocs.length - 1];
            let offset = upperRow.offsetTop + upperRow.offsetHeight;
            if (loc.y > offset + this.options.tolerance) {
                return 0;
            }
            let dist = 0;
            if (lowerRow) {
                let upperDist = Math.abs(loc.x - upperRow.offsetLeft - upperRow.offsetWidth / 2);
                let lowerDist = Math.abs(loc.x - lowerRow.offsetLeft - lowerRow.offsetWidth / 2);
                dist = Math.max(upperDist, lowerDist);
            } else {
                dist = Math.abs(loc.x - upperRow.offsetLeft - upperRow.offsetWidth / 2);
            }
            return dist < this.options.tolerance ? 0 : this.options.tolerance - dist;
        };

        let rows = this.menu.querySelectorAll(this.rowSelector);
        for (let i = 0; i < rows.length; i++) {
            let row = rows[i];
            row.classList.remove(this.activeClass);
            row.addEventListener("mouseenter", this.mouseenterRow.bind(this, row));
            row.addEventListener("mouseleave", this.mouseleaveRow.bind(this, row));
            row.addEventListener("click", this.clickRow.bind(this, row));
        }

        document.addEventListener("mousemove", this.mousemoveDocument.bind(this));
        this.menu.addEventListener("mouseleave", this.mouseleaveMenu.bind(this));
    }

    option(key, value) {
        this.options[key] = value;
        return this;
    }
}
