import { Scroll } from './Scroll'

function i (scroll, t, n) {
  function i (t, scroll, r, o) {
    if (!t || !t.cancelled) {
      r(scroll)
      var a = scroll.done()
      if (!a) {
        if (!t.cancelled) {
          t.id = requestAnimationFrame(i.bind(null, t, scroll, r, o))
        }
      }
      if (a && o) {
        o(scroll)
      }
    }
  }

  function r (scroll) {
    if (scroll && scroll.id) {
      cancelAnimationFrame(scroll.id)
    }
    if (scroll) {
      scroll.cancelled = true
    }
  }
  var o = {
    id: 0,
    cancelled: false
  }
  i(o, scroll, t, n)
  return {
    cancel: r.bind(null, o),
    model: scroll
  }
}

export function Scroller (element, options) {
  options = options || {}
  this._element = element
  this._options = options
  this._enableSnap = options.enableSnap || false
  this._itemSize = options.itemSize || 0
  this._enableX = options.enableX || false
  this._enableY = options.enableY || false
  this._shouldDispatchScrollEvent = !!options.onScroll
  if (this._enableX) {
    this._extent = (options.scrollWidth || this._element.offsetWidth) - this._element.parentElement.offsetWidth
    this._scrollWidth = options.scrollWidth
  } else {
    this._extent = (options.scrollHeight || this._element.offsetHeight) - this._element.parentElement.offsetHeight
    this._scrollHeight = options.scrollHeight
  }

  this._position = 0
  this._scroll = new Scroll(this._extent, options.friction, options.spring)
  this._onTransitionEnd = this.onTransitionEnd.bind(this)
  this.updatePosition()
}

Scroller.prototype.onTouchStart = function () {
  this._startPosition = this._position
  this._lastChangePos = this._startPosition
  if (this._startPosition > 0) {
    this._startPosition /= 0.5
  } else {
    if (this._startPosition < -this._extent) {
      this._startPosition = (this._startPosition + this._extent) / 0.5 - this._extent
    }
  }
  if (this._animation) {
    this._animation.cancel()
    this._scrolling = false
  }
  this.updatePosition()
}
Scroller.prototype.onTouchMove = function (x, y) {
  var startPosition = this._startPosition
  if (this._enableX) {
    startPosition += x
  } else if (this._enableY) {
    startPosition += y
  }

  if (startPosition > 0) {
    startPosition *= 0.5
  } else if (startPosition < -this._extent) {
    startPosition = 0.5 * (startPosition + this._extent) - this._extent
  }
  this._position = startPosition
  this.updatePosition()
  this.dispatchScroll()
}
Scroller.prototype.onTouchEnd = function (e, r, o) {
  if (this._enableSnap && this._position > -this._extent && this._position < 0) {
    if (this._enableY && ((Math.abs(r) < this._itemSize && Math.abs(o.y) < 300) || Math.abs(o.y) < 150)) {
      this.snap()
      return
    }
    if (this._enableX && ((Math.abs(e) < this._itemSize && Math.abs(o.x) < 300) || Math.abs(o.x) < 150)) {
      this.snap()
      return
    }
  }
  if (this._enableX) {
    this._scroll.set(this._position, o.x)
  } else if (this._enableY) {
    this._scroll.set(this._position, o.y)
  }

  if (this._enableSnap) {
    var s = this._scroll._friction.x(100)
    var l = s % this._itemSize
    var c = Math.abs(l) > this._itemSize / 2 ? s - (this._itemSize - Math.abs(l)) : s - l
    if (c <= 0 && c >= -this._extent) {
      this._scroll.setVelocityByEnd(c)
    }
  }
  this._lastTime = Date.now()
  this._lastDelay = 0
  this._scrolling = true
  this._lastChangePos = this._position
  this._lastIdx = Math.floor(Math.abs(this._position / this._itemSize))
  this._animation = i(this._scroll, () => {
    var e = Date.now()
    var i = (e - this._scroll._startTime) / 1e3
    var r = this._scroll.x(i)
    this._position = r
    this.updatePosition()
    var o = this._scroll.dx(i)
    if (this._shouldDispatchScrollEvent && e - this._lastTime > this._lastDelay) {
      this.dispatchScroll()
      this._lastDelay = Math.abs(2e3 / o)
      this._lastTime = e
    }
  }, () => {
    if (this._enableSnap) {
      if (c <= 0 && c >= -this._extent) {
        this._position = c
        this.updatePosition()
      }
      if (typeof this._options.onSnap === 'function') {
        this._options.onSnap(Math.floor(Math.abs(this._position) / this._itemSize))
      }
    }
    if (this._shouldDispatchScrollEvent) {
      this.dispatchScroll()
    }
    this._scrolling = false
  })
}
Scroller.prototype.onTransitionEnd = function () {
  this._element.style.transition = ''
  this._element.style.webkitTransition = ''
  this._element.removeEventListener('transitionend', this._onTransitionEnd)
  this._element.removeEventListener('webkitTransitionEnd', this._onTransitionEnd)
  if (this._snapping) {
    this._snapping = false
  }
  this.dispatchScroll()
}
Scroller.prototype.snap = function () {
  var e = this._itemSize
  var t = this._position % e
  var i = Math.abs(t) > this._itemSize / 2 ? this._position - (e - Math.abs(t)) : this._position - t
  if (this._position !== i) {
    this._snapping = true
    this.scrollTo(-i)
    if (typeof this._options.onSnap === 'function') {
      this._options.onSnap(Math.floor(Math.abs(this._position) / this._itemSize))
    }
  }
}
Scroller.prototype.scrollTo = function (e, t) {
  if (this._animation) {
    this._animation.cancel()
    this._scrolling = false
  }
  if (typeof e === 'number') {
    this._position = -e
  }
  if (this._position < -this._extent) {
    this._position = -this._extent
  } else {
    if (this._position > 0) {
      this._position = 0
    }
  }
  this._element.style.transition = 'transform ' + (t || 0.2) + 's ease-out'
  this._element.style.webkitTransition = '-webkit-transform ' + (t || 0.2) + 's ease-out'
  this.updatePosition()
  this._element.addEventListener('transitionend', this._onTransitionEnd)
  this._element.addEventListener('webkitTransitionEnd', this._onTransitionEnd)
}
Scroller.prototype.dispatchScroll = function () {
  if (typeof this._options.onScroll === 'function' && Math.round(this._lastPos) !== Math.round(this._position)) {
    this._lastPos = this._position
    var e = {
      target: {
        scrollLeft: this._enableX ? -this._position : 0,
        scrollTop: this._enableY ? -this._position : 0,
        scrollHeight: this._scrollHeight || this._element.offsetHeight,
        scrollWidth: this._scrollWidth || this._element.offsetWidth,
        offsetHeight: this._element.parentElement.offsetHeight,
        offsetWidth: this._element.parentElement.offsetWidth
      }
    }
    this._options.onScroll(e)
  }
}
Scroller.prototype.update = function (e, t, n) {
  var i = 0
  var r = this._position
  if (this._enableX) {
    i = this._element.childNodes.length ? (t || this._element.offsetWidth) - this._element.parentElement.offsetWidth : 0
    this._scrollWidth = t
  } else {
    i = this._element.childNodes.length ? (t || this._element.offsetHeight) - this._element.parentElement.offsetHeight : 0
    this._scrollHeight = t
  }
  if (typeof e === 'number') {
    this._position = -e
  }
  if (this._position < -i) {
    this._position = -i
  } else {
    if (this._position > 0) {
      this._position = 0
    }
  }
  this._itemSize = n || this._itemSize
  this.updatePosition()
  if (r !== this._position) {
    this.dispatchScroll()
    if (typeof this._options.onSnap === 'function') {
      this._options.onSnap(Math.floor(Math.abs(this._position) / this._itemSize))
    }
  }
  this._extent = i
  this._scroll._extent = i
}
Scroller.prototype.updatePosition = function () {
  var transform = ''
  if (this._enableX) {
    transform = 'translateX(' + this._position + 'px) translateZ(0)'
  } else {
    if (this._enableY) {
      transform = 'translateY(' + this._position + 'px) translateZ(0)'
    }
  }
  this._element.style.webkitTransform = transform
  this._element.style.transform = transform
}
Scroller.prototype.isScrolling = function () {
  return this._scrolling || this._snapping
}
