import anime from 'animejs'

interface Options {
  mode: keyof typeof animationModes
  duration: number
  speed: number
  stagger: number
  reverse: boolean
}

const animationModes = {
  fade: {
    in: {
      opacity: [0, 1],
      easing: 'linear',
    },
    out: {
      opacity: [1, 0],
      easing: 'linear',
    },
    init: {
      opacity: 0,
    },
  },
  slideRight: {
    in: {
      translateX: () => {
        return [`-100vw`, '0vw']
      },
      easing: 'easeOutQuad',
    },
    out: {
      translateX: () => {
        return ['0vw', `100vw`]
      },
      easing: 'easeInQuad',
    },
    init: {
      transform: 'translateX(-100vw)',
    },
  },
  slideLeft: {
    in: {
      translateX: () => {
        return [`100vw`, '0vw']
      },
      easing: 'easeOutQuad',
    },
    out: {
      translateX: () => {
        return ['0vw', `-100vw`]
      },
      easing: 'easeInQuad',
    },
    init: {
      transform: 'translateX(100vw)',
    },
  },
}

export class AnimationController {
  public mode: any
  public duration: number
  public speed: number
  public stagger: number
  public reverse: boolean

  constructor() {
    this.mode = animationModes.fade
    this.duration = 10000
    this.speed = 1000
    this.stagger = 1000 / 4
    this.reverse = false
  }

  configure(options: Partial<Options>) {
    this.mode = options.mode && animationModes.hasOwnProperty(options.mode) ? animationModes[options.mode] : animationModes.fade
    this.duration = options.duration ?? 10000
    this.speed = 1000 * (options.speed ?? 1)
    this.stagger = this.speed / (options.stagger ?? 4)
    this.reverse = options.reverse ?? false
  }

  getInitialStyle() {
    return this.mode.init
  }

  animate(listNodes: any, finished: any) {
    let self = this
    let els = [].slice.call(listNodes, 0)
    if (this.reverse) els = els.reverse()
    if (!els || els.length < 1) return
    let offset = this.duration - ((els.length - 1) * this.stagger + this.speed) * 2
    offset = offset < 0 ? 0 : offset
    let animation = anime.timeline({
      autoplay: false,
    })
    animation.add(
      {
        targets: els,
        duration: this.speed,
        delay: function (_el: any, i: any, _l: any) {
          return i * self.stagger
        },
        ...this.mode.in
      }
    )
    animation.add(
      {
        targets: els,
        duration: this.speed,
        delay: function (_el: any, _i: any, _l: any) {
          return _i * self.stagger
        },
        complete: () => {
          finished()
        },
        ...this.mode.out
      },
      `+=${offset}`
    )
    animation.play()
  }
}
