var transform = (function() {
  var properties = [
    'oTransform', // Opera 11.5
    'msTransform', // IE 9
    'mozTransform',
    'webkitTransform',
    'transform'
  ];
  var style = document.createElement('div').style;
  for (var i = 0; i < properties.length; i++) {
    /* istanbul ignore else */
    if (properties[i] in style) {
      return properties[i];
    }
  }
  /* istanbul ignore next */
  return 'transform';
}());

function createCommentNode(cmt) {
  var node = document.createElement('div');
  node.style.cssText = 'position:absolute;';
  if (typeof cmt.render === 'function') {
    var $el = cmt.render();
    if ($el instanceof HTMLElement) {
      node.appendChild($el);
      return node;
    }
  }
  node.textContent = cmt.text;
  if (cmt.style) {
    for (var key in cmt.style) {
      node.style[key] = cmt.style[key];
    }
  }
  return node;
}

function init() {
  var stage = document.createElement('div');
  stage.style.cssText = 'overflow:hidden;white-space:nowrap;transform:translateZ(0);';
  return stage;
}

function clear(stage) {
  var lc = stage.lastChild;
  while (lc) {
    stage.removeChild(lc);
    lc = stage.lastChild;
  }
}

function resize(stage) {
  stage.style.width = stage.width + 'px';
  stage.style.height = stage.height + 'px';
}

function framing() {
  //
}

function setup(stage, comments) {
  var df = document.createDocumentFragment();
  var i = 0;
  var cmt = null;
  for (i = 0; i < comments.length; i++) {
    cmt = comments[i];
    cmt.node = cmt.node || createCommentNode(cmt);
    df.appendChild(cmt.node);
  }
  if (comments.length) {
    stage.appendChild(df);
  }
  for (i = 0; i < comments.length; i++) {
    cmt = comments[i];
    cmt.width = cmt.width || cmt.node.offsetWidth;
    cmt.height = cmt.height || cmt.node.offsetHeight;
  }
}

function render(stage, cmt) {
  cmt.node.style[transform] = 'translate(' + cmt.x + 'px,' + cmt.y + 'px)';
}

/* eslint no-invalid-this: 0 */
function remove(stage, cmt) {
  stage.removeChild(cmt.node);
  /* istanbul ignore else */
  if (!this.media) {
    cmt.node = null;
  }
}

var domEngine = {
  name: 'dom',
  init: init,
  clear: clear,
  resize: resize,
  framing: framing,
  setup: setup,
  render: render,
  remove: remove,
};

/* eslint no-invalid-this: 0 */
function allocate(cmt) {
  var that = this;
  var ct = this.media ? this.media.currentTime : Date.now() / 1000;
  var pbr = this.media ? this.media.playbackRate : 1;
  function willCollide(cr, cmt) {
    if (cmt.mode === 'top' || cmt.mode === 'bottom') {
      return ct - cr.time < that._.duration;
    }
    var crTotalWidth = that._.stage.width + cr.width;
    var crElapsed = crTotalWidth * (ct - cr.time) * pbr / that._.duration;
    if (cr.width > crElapsed) {
      return true;
    }
    // (rtl mode) the right end of `cr` move out of left side of stage
    var crLeftTime = that._.duration + cr.time - ct;
    var cmtTotalWidth = that._.stage.width + cmt.width;
    var cmtTime = that.media ? cmt.time : cmt._utc;
    var cmtElapsed = cmtTotalWidth * (ct - cmtTime) * pbr / that._.duration;
    var cmtArrival = that._.stage.width - cmtElapsed;
    // (rtl mode) the left end of `cmt` reach the left side of stage
    var cmtArrivalTime = that._.duration * cmtArrival / (that._.stage.width + cmt.width);
    return crLeftTime > cmtArrivalTime;
  }
  var crs = this._.space[cmt.mode];
  var last = 0;
  var curr = 0;
  for (var i = 1; i < crs.length; i++) {
    var cr = crs[i];
    var requiredRange = cmt.height;
    if (cmt.mode === 'top' || cmt.mode === 'bottom') {
      requiredRange += cr.height;
    }
    if (cr.range - cr.height - crs[last].range >= requiredRange) {
      curr = i;
      break;
    }
    if (willCollide(cr, cmt)) {
      last = i;
    }
  }
  var channel = crs[last].range;
  var crObj = {
    range: channel + cmt.height,
    time: this.media ? cmt.time : cmt._utc,
    width: cmt.width,
    height: cmt.height
  };
  crs.splice(last + 1, curr - last - 1, crObj);

  if (cmt.mode === 'bottom') {
    return this._.stage.height - cmt.height - channel % this._.stage.height;
  }
  return channel % (this._.stage.height - cmt.height);
}

/* eslint no-invalid-this: 0 */
function createEngine(framing, setup, render, remove) {
  return function() {
    framing(this._.stage);
    var dn = Date.now() / 1000;
    var ct = this.media ? this.media.currentTime : dn;
    var pbr = this.media ? this.media.playbackRate : 1;
    var cmt = null;
    var cmtt = 0;
    var i = 0;
    for (i = this._.runningList.length - 1; i >= 0; i--) {
      cmt = this._.runningList[i];
      cmtt = this.media ? cmt.time : cmt._utc;
      if (ct - cmtt > this._.duration) {
        remove(this._.stage, cmt);
        this._.runningList.splice(i, 1);
      }
    }
    var pendingList = [];
    while (this._.position < this.comments.length) {
      cmt = this.comments[this._.position];
      cmtt = this.media ? cmt.time : cmt._utc;
      if (cmtt >= ct) {
        break;
      }
      // when clicking controls to seek, media.currentTime may changed before
      // `pause` event is fired, so here skips comments out of duration,
      // see https://github.com/weizhenye/Danmaku/pull/30 for details.
      if (ct - cmtt > this._.duration) {
        ++this._.position;
        continue;
      }
      if (this.media) {
        cmt._utc = dn - (this.media.currentTime - cmt.time);
      }
      pendingList.push(cmt);
      ++this._.position;
    }
    setup(this._.stage, pendingList);
    for (i = 0; i < pendingList.length; i++) {
      cmt = pendingList[i];
      cmt.y = allocate.call(this, cmt);
      if (cmt.mode === 'top' || cmt.mode === 'bottom') {
        cmt.x = (this._.stage.width - cmt.width) >> 1;
      }
      this._.runningList.push(cmt);
    }
    for (i = 0; i < this._.runningList.length; i++) {
      cmt = this._.runningList[i];
      var totalWidth = this._.stage.width + cmt.width;
      var elapsed = totalWidth * (dn - cmt._utc) * pbr / this._.duration;
      if (cmt.mode === 'ltr') cmt.x = (elapsed - cmt.width + .5) | 0;
      if (cmt.mode === 'rtl') cmt.x = (this._.stage.width - elapsed + .5) | 0;
      render(this._.stage, cmt);
    }
  };
}

var raf =
  window.requestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  function(cb) {
    return setTimeout(cb, 50 / 3);
  };

var caf =
  window.cancelAnimationFrame ||
  window.mozCancelAnimationFrame ||
  window.webkitCancelAnimationFrame ||
  clearTimeout;

function binsearch(arr, prop, key) {
  var mid = 0;
  var left = 0;
  var right = arr.length;
  while (left < right - 1) {
    mid = (left + right) >> 1;
    if (key >= arr[mid][prop]) {
      left = mid;
    } else {
      right = mid;
    }
  }
  if (arr[left] && key < arr[left][prop]) {
    return left;
  }
  return right;
}


function formatMode(mode) {
  if (!/^(ltr|top|bottom)$/i.test(mode)) {
    return 'rtl';
  }
  return mode.toLowerCase();
}

function collidableRange() {
  var max = 9007199254740991;
  return [
    { range: 0, time: -max, width: max, height: 0 },
    { range: max, time: max, width: 0, height: 0 }
  ];
}

function resetSpace(space) {
  space.ltr = collidableRange();
  space.rtl = collidableRange();
  space.top = collidableRange();
  space.bottom = collidableRange();
}

/* eslint no-invalid-this: 0 */
function play() {
  if (!this._.visible || !this._.paused) {
    return this;
  }
  this._.paused = false;
  if (this.media) 

  var that = this;
  var engine = createEngine(
    this._.engine.framing.bind(this),
    this._.engine.setup.bind(this),
    this._.engine.render.bind(this),
    this._.engine.remove.bind(this)
  );
  function frame() {
    engine.call(that);
    that._.requestID = raf(frame);
  }
  this._.requestID = raf(frame);
  return this;
}

/* eslint no-invalid-this: 0 */
function pause() {
  if (!this._.visible || this._.paused) {
    return this;
  }
  this._.paused = true;
  caf(this._.requestID);
  this._.requestID = 0;
  return this;
}

/* eslint no-invalid-this: 0 */
function seek() {
  if (!this.media) {
    return this;
  }
  this.clear();
  resetSpace(this._.space);
  var position = binsearch(this.comments, 'time', this.media.currentTime);
  this._.position = Math.max(0, position - 1);
  return this;
}

/* eslint no-invalid-this: 0 */
function bindEvents(_) {
  _.play = play.bind(this);
  _.pause = pause.bind(this);
  _.seeking = seek.bind(this);
  this.media.addEventListener('play', _.play);
  this.media.addEventListener('pause', _.pause);
  this.media.addEventListener('playing', _.play);
  this.media.addEventListener('waiting', _.pause);
  this.media.addEventListener('seeking', _.seeking);
}

/* eslint no-invalid-this: 0 */
function unbindEvents(_) {
  this.media.removeEventListener('play', _.play);
  this.media.removeEventListener('pause', _.pause);
  this.media.removeEventListener('playing', _.play);
  this.media.removeEventListener('waiting', _.pause);
  this.media.removeEventListener('seeking', _.seeking);
  _.play = null;
  _.pause = null;
  _.seeking = null;
}

/* eslint-disable no-invalid-this */
function init$1(opt) {
  this._ = {};
  this.container = opt.container || document.createElement('div');
  this.media = opt.media;
  this._.visible = true;

  /* eslint-disable no-undef */
  /* istanbul ignore next */
  {
    this.engine = 'dom';
    this._.engine = domEngine;
  }
  /* eslint-enable no-undef */
  this._.requestID = 0;

  this._.speed = Math.max(0, opt.speed) || 144;
  this._.duration = 4;

  this.comments = opt.comments || [];
  this.comments.sort(function(a, b) {
    return a.time - b.time;
  });
  for (var i = 0; i < this.comments.length; i++) {
    this.comments[i].mode = formatMode(this.comments[i].mode);
  }
  this._.runningList = [];
  this._.position = 0;

  this._.paused = true;
  if (this.media) {
    this._.listener = {};
    bindEvents.call(this, this._.listener);
  }

  this._.stage = this._.engine.init(this.container);
  this._.stage.style.cssText += 'position:relative;pointer-events:none;';

  this.resize();
  this.container.appendChild(this._.stage);

  this._.space = {};
  resetSpace(this._.space);

  if (!this.media || !this.media.paused) {
    seek.call(this);
    play.call(this);
  }
  return this;
}

/* eslint-disable no-invalid-this */
function destroy() {
  if (!this.container) {
    return this;
  }

  pause.call(this);
  this.clear();
  this.container.removeChild(this._.stage);
  if (this.media) {
    unbindEvents.call(this, this._.listener);
  }
  for (var key in this) {
    /* istanbul ignore else  */
    if (Object.prototype.hasOwnProperty.call(this, key)) {
      this[key] = null;
    }
  }
  return this;
}

var properties = ['mode', 'time', 'text', 'render', 'style'];

/* eslint-disable no-invalid-this */
function emit(obj) {
  if (!obj || Object.prototype.toString.call(obj) !== '[object Object]') {
    return this;
  }
  var cmt = {};
  for (var i = 0; i < properties.length; i++) {
    if (obj[properties[i]] !== undefined) {
      cmt[properties[i]] = obj[properties[i]];
    }
  }
  cmt.text = (cmt.text || '').toString();
  cmt.mode = formatMode(cmt.mode);
  cmt._utc = Date.now() / 1000;
  if (this.media) {
    var position = 0;
    if (cmt.time === undefined) {
      cmt.time = this.media.currentTime;
      position = this._.position;
    } else {
      position = binsearch(this.comments, 'time', cmt.time);
      if (position < this._.position) {
        this._.position += 1;
      }
    }
    this.comments.splice(position, 0, cmt);
  } else {
    this.comments.push(cmt);
  }
  return this;
}

/* eslint-disable no-invalid-this */
function show() {
  if (this._.visible) {
    return this;
  }
  this._.visible = true;
  if (this.media && this.media.paused) {
    return this;
  }
  seek.call(this);
  play.call(this);
  return this;
}

/* eslint-disable no-invalid-this */
function hide() {
  if (!this._.visible) {
    return this;
  }
  pause.call(this);
  this.clear();
  this._.visible = false;
  return this;
}

/* eslint-disable no-invalid-this */
function clear$1() {
  this._.engine.clear(this._.stage, this._.runningList);
  this._.runningList = [];
  return this;
}

/* eslint-disable no-invalid-this */
function resize$1() {
  this._.stage.width = this.container.offsetWidth;
  this._.stage.height = this.container.offsetHeight;
  this._.engine.resize(this._.stage);
  this._.duration = this._.stage.width / this._.speed;
  return this;
}

var speed = {
  get: function() {
    return this._.speed;
  },
  set: function(s) {
    if (typeof s !== 'number' ||
      isNaN(s) ||
      !isFinite(s) ||
      s <= 0) {
      return this._.speed;
    }
    this._.speed = s;
    if (this._.stage.width) {
      this._.duration = this._.stage.width / s;
    }
    return s;
  }
};

function Danmaku(opt) {
  opt && init$1.call(this, opt);
}
Danmaku.prototype.destroy = function() {
  return destroy.call(this);
};
Danmaku.prototype.emit = function(cmt) {
  return emit.call(this, cmt);
};
Danmaku.prototype.show = function() {
  return show.call(this);
};
Danmaku.prototype.hide = function() {
  return hide.call(this);
};
Danmaku.prototype.clear = function() {
  return clear$1.call(this);
};
Danmaku.prototype.resize = function() {
  return resize$1.call(this);
};
Object.defineProperty(Danmaku.prototype, 'speed', speed);

export default Danmaku;

{
    for (var i = 0; i < this._.runningList.length; i++) {
      var cmt = this._.runningList[i];
      cmt._utc = Date.now() / 1000 - (this.media.currentTime - cmt.time);
    }
  }