/* eslint-disable wrap-iife, func-names, strict, no-var, vars-on-top, no-param-reassign, object-shorthand, no-shadow, comma-dangle, prefer-template, consistent-return, no-mixed-operators, no-unused-vars, no-unused-expressions, prefer-arrow-callback, max-len */
(function () {
  'use strict';

  function simulateEvent(el, type, options) {
    var event;
    if (!el) return;
    var ownerDocument = el.ownerDocument;

    options = options || {};

    if (/^mouse/.test(type)) {
      event = ownerDocument.createEvent('MouseEvents');
      event.initMouseEvent(type, true, true, ownerDocument.defaultView,
      options.button, options.screenX, options.screenY, options.clientX, options.clientY,
      options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
    } else {
      event = ownerDocument.createEvent('CustomEvent');

      event.initCustomEvent(type, true, true, ownerDocument.defaultView,
      options.button, options.screenX, options.screenY, options.clientX, options.clientY,
      options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);

      event.dataTransfer = {
        data: {},

        setData: function (type, val) {
          this.data[type] = val;
        },

        getData: function (type) {
          return this.data[type];
        }
      };
    }

    if (el.dispatchEvent) {
      el.dispatchEvent(event);
    } else if (el.fireEvent) {
      el.fireEvent('on' + type, event);
    }

    return event;
  }

  function isLast(target) {
    var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
    var children = el.children;

    return children.length - 1 === target.index;
  }

  function getTarget(target) {
    var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
    var children = el.children;

    return (
      children[target.index] ||
      children[target.index === 'first' ? 0 : -1] ||
      children[target.index === 'last' ? children.length - 1 : -1] ||
      el
    );
  }

  function getRect(el) {
    var rect = el.getBoundingClientRect();
    var width = rect.right - rect.left;
    var height = rect.bottom - rect.top + 10;

    return {
      x: rect.left,
      y: rect.top,
      cx: rect.left + width / 2,
      cy: rect.top + height / 2,
      w: width,
      h: height,
      hw: width / 2,
      wh: height / 2
    };
  }

  function simulateDrag(options, callback) {
    options.to.el = options.to.el || options.from.el;

    var fromEl = getTarget(options.from);
    var toEl = getTarget(options.to);
    var firstEl = getTarget({
      el: options.to.el,
      index: 'first'
    });
    var lastEl = getTarget({
      el: options.to.el,
      index: 'last'
    });
    var scrollable = options.scrollable;

    var fromRect = getRect(fromEl);
    var toRect = getRect(toEl);
    var firstRect = getRect(firstEl);
    var lastRect = getRect(lastEl);

    var startTime = new Date().getTime();
    var duration = options.duration || 1000;
    simulateEvent(fromEl, 'mousedown', { button: 0 });
    options.ontap && options.ontap();
    window.SIMULATE_DRAG_ACTIVE = 1;

    if (options.to.index === 0) {
      toRect.cy = firstRect.y;
    } else if (isLast(options.to)) {
      toRect.cy = lastRect.y + lastRect.h + 50;
    }

    var dragInterval = setInterval(function loop() {
      var progress = (new Date().getTime() - startTime) / duration;
      var x = (fromRect.cx + (toRect.cx - fromRect.cx) * progress) - scrollable.scrollLeft;
      var y = (fromRect.cy + (toRect.cy - fromRect.cy) * progress) - scrollable.scrollTop;
      var overEl = fromEl.ownerDocument.elementFromPoint(x, y);

      simulateEvent(overEl, 'mousemove', {
        clientX: x,
        clientY: y
      });

      if (progress >= 1) {
        options.ondragend && options.ondragend();
        simulateEvent(toEl, 'mouseup');
        clearInterval(dragInterval);
        window.SIMULATE_DRAG_ACTIVE = 0;
      }
    }, 100);

    return {
      target: fromEl,
      fromList: fromEl.parentNode,
      toList: toEl.parentNode
    };
  }

  // Export
  window.simulateEvent = simulateEvent;
  window.simulateDrag = simulateDrag;
})();
