export interface DeviceMotionControllerCallbacks {
  onSample: (alpha: number | null, beta: number | null, gamma: number | null) => void
  onError: (message: string) => void
}

export class DeviceMotionController {
  callbacks: DeviceMotionControllerCallbacks
  listening: boolean
  starting: boolean
  motionCallback: ((result: WechatMiniprogram.OnDeviceMotionChangeCallbackResult) => void) | null

  constructor(callbacks: DeviceMotionControllerCallbacks) {
    this.callbacks = callbacks
    this.listening = false
    this.starting = false
    this.motionCallback = null
  }

  start(): void {
    if (this.listening || this.starting) {
      return
    }

    if (typeof wx.startDeviceMotionListening !== 'function' || typeof wx.onDeviceMotionChange !== 'function') {
      this.callbacks.onError('当前环境不支持设备方向监听')
      return
    }

    const callback = (result: WechatMiniprogram.OnDeviceMotionChangeCallbackResult) => {
      const alpha = typeof result.alpha === 'number' && !Number.isNaN(result.alpha) ? result.alpha : null
      const beta = typeof result.beta === 'number' && !Number.isNaN(result.beta) ? result.beta : null
      const gamma = typeof result.gamma === 'number' && !Number.isNaN(result.gamma) ? result.gamma : null
      this.callbacks.onSample(alpha, beta, gamma)
    }

    this.motionCallback = callback
    wx.onDeviceMotionChange(callback)
    this.starting = true
    wx.startDeviceMotionListening({
      interval: 'game',
      success: () => {
        this.starting = false
        this.listening = true
      },
      fail: (res) => {
        this.starting = false
        this.detachCallback()
        this.callbacks.onError(res && res.errMsg ? res.errMsg : 'startDeviceMotionListening failed')
      },
    })
  }

  stop(): void {
    this.detachCallback()
    wx.stopDeviceMotionListening({
      complete: () => {},
    })
    this.starting = false
    this.listening = false
  }

  destroy(): void {
    this.stop()
  }

  private detachCallback(): void {
    if (!this.motionCallback) {
      return
    }

    if (typeof wx.offDeviceMotionChange === 'function') {
      wx.offDeviceMotionChange(this.motionCallback)
    }

    this.motionCallback = null
  }
}
