/**
 *
 * 修改自:
 * https://github.com/zya/wa-metro/blob/master/lib/wa-metro.js
 *
 */

import runtime, * as RunTimeUtils from '/src/pages/detail/runtime'
import state from '/src/pages/detail/state'

type ICallback = (time?: number, step?: number, scheduler_interval?: number, context?: AudioContext) => void

type IOptions = {
  steps: number
  tempo: number
  denominator: number
  numerator: number
  limit: number
  onStop?: () => void
  onTick?: (tick?: number) => void
}

export const ac = (window.AudioContext ||  (window as any).webkitAudioContext || (window as any).mozAudioContext || (window as any).msAudioContext)

 class Metro {
  audio: HTMLAudioElement = new Audio()//runtime.audiosInstance.audio.cloneNode(true)
  context = new ac()
  callback: ICallback = () => {}
  look_ahead = 1.0
  _step = 1
  _scheduler_interval = 20
  _next_event_time = 0.0
  _first = true
  _is_running = false
  timer: number = -1
  tiems: number = 0
  tick: number = 0

  options: IOptions = {
    steps: 4,
    tempo: 90,
    denominator: 4,
    numerator: 4,
    limit: 0,
    onStop: () => {},
    onTick: () => {}
  }

   constructor(options?: IOptions) {
    this.options = {
      ...this.options,
      ...options
    }
   }

   refreshTick = () => {
      RunTimeUtils.refreshTick(this.audio.currentTime)
      if (this.audio.currentTime >= 4.8) {
        this.stop()
      }
   }

   start = (options?: IOptions) => {
    // state.showTick = true
    this.audio.play()
    this.audio.addEventListener('timeupdate', this.refreshTick)
    // if (this._is_running) {
    //   console.log('already started');
    //   return;
    // }
    // this._step = 1
    this.options = {
      ...this.options,
      ...options
    }
    // this._is_running = true;
    // this.timer = setInterval(() => {
    //   this._scheduler()
    // }, 25)
   //  this._worker.postMessage('start');
  };

  pause = () => {
    this._is_running = false;
   //  this._worker.postMessage('stop');
   this.stop()
  };

  stop = (dontCall?: boolean) => {
    // state.showTick = false
    this.audio.pause()
    this.audio.removeEventListener('timeupdate', this.refreshTick)
    if (this.audio.currentTime >= 4.8) {
      this.options.onStop && this.options.onStop()
    }
    runtime.osmd.cursor.hide()
    this.audio.currentTime = 0
    this.refreshTick()
    // this._first = true;
    // this._step = 1;
    // this._is_running = false;
    // clearInterval(this.timer)
    // this.timer = -1
    // this.tiems = 0
    // this.tick = 0
    // this.context = new ac()
    // if (dontCall !== true) {
    //   setTimeout(() => {
    //     this._is_running = false;
    //     this.options.onStop && this.options.onStop()
    //     this.context.close()
    //   }, 500)
    // } else {
    //   // this.context.close()
    // }
   //  this._worker.postMessage('stop');
  };

  scheduleNote = (beatNumber: number, time: number) => {
    // create an oscillator
    var osc = this.context.createOscillator();
    osc.connect( this.context.destination );
    // if (beatNumber % 16 === 0)    // beat 0 == low pitch
    //     osc.frequency.value = 880.0;
    // osc.type = 'sine';
    // osc.detune.value = -300;
    if (beatNumber === 1) {
      osc.frequency.value = 880.0;
    } else {
      osc.frequency.value = 440.0;
    }
    osc.start( time );
    osc.stop( time + 0.05 );
    // osc.onended = () => {
    //   console.log(time)
    //   this.tick ++
    //   this.options.onTick && this.options.onTick(this.tick);
    //   // if (this.options.limit > 0 && this.tick === this.options.limit * this.options.numerator) {
    //   //   this.options.onStop && this.options.onStop()
    //   // }
    // }
  }

  _scheduler = () => {
    var self = this;
    if (this._step === 1 && this._first) {
      this._next_event_time = this.context.currentTime;
    }
    while (this._next_event_time < this.context.currentTime + this.look_ahead && this._is_running) {
      if (this.options.limit > 0 && this.tiems >= this.options.limit) {
        this.stop()
        break
      }
      if (this._step === this.options.numerator) {
        this.tiems ++
      }
      this.options.onTick && this.options.onTick(this._step);
      // var event_time_from_scheduled = self._next_event_time - self.context.currentTime;
      this.scheduleNote(this._step, self._next_event_time)
      this._next();
    }
  };

  _next = () => {
    this._step++;
    if (this._first) {
      this._next_event_time = this.context.currentTime;
      this._first = false;
    }
      // if (this._step > this.options.numerator) {
      //   this._step = 1;
      // }
    this._next_event_time += ((60.0 / this.options.tempo) * 4) / this.options.denominator;
  };
 }

export default Metro
