const SECONDS_TO_MICOSECONDS = 1000000;
var MyAudioPlayer;
if (typeof wx != "undefined") {
  MyAudioPlayer = require("audio_player.js");
} else {
  MyAudioPlayer = AudioPlayer;
}
class AudioPlayerAssistant {
  constructor(audioContext, wasmFS) {
    this.isWx = typeof wx != "undefined";
    this.audioPlayer = new MyAudioPlayer(audioContext, wasmFS);
  }

  initialize(renderFunc) {
    this.renderFunc = renderFunc;
    this.onStatusChanged = null;
    this.stopRoll = true;
    this.renderTimeLine = 0;
    this.currentFrameNumber = 0;
  }

  getCurrentTime() {
    if (this.isWx) {
      return wx.getPerformance().now();
    }

    return performance.now();
  }

  renderVideo() {
    let that = this;
    if (!that.continueRender) {
      return;
    }

    that.frameStartTime = that.getCurrentTime();
    let videoTime = that.renderFunc(this.renderTimeLine);
    if (videoTime < 0) {
      return;
    }
    ++that.currentFrameNumber;

    let realAudioTime = that.audioPlayer.getCurrentTime();
    // console.log("audio player tick: time=", realAudioTime, "return time=", videoTime);

    let nextTick = true;
    if (realAudioTime * SECONDS_TO_MICOSECONDS - videoTime > 1000000) {
      nextTick = false;
      that.audioPlayer.pause(()=>{
        that.stopRoll = false;
        let targetTime = that.audioPlayer.getCurrentTime() * SECONDS_TO_MICOSECONDS;
        console.log("start roll targetTime=:", targetTime, " ,currentTime=", videoTime);
        that.startRoll(targetTime, (videoTime)=>{
          return videoTime + SECONDS_TO_MICOSECONDS / that.renderFPS  >= targetTime;
        }, () => {
          that.renderTimeLine = targetTime;
          if (that.audioPlayer.status != MyAudioPlayer.STATUS.stopping &&
              that.audioPlayer.status != MyAudioPlayer.STATUS.stopped) {
            that.play();
          }
        });
      });
    } 

    if (realAudioTime >= that.duration() && videoTime < that.renderDuration) {
      nextTick = false;
      that.stopRoll = false;
      that.startRoll(that.renderDuration, (videoTime)=>{
        return videoTime >= that.renderDuration;
      }, () => {
        that.renderTimeLine = that.renderDuration;
      });
    }

    if (nextTick) {
      let costTime = that.getCurrentTime() - that.frameStartTime;
      let renderTime = that.renderDelta - (Math.ceil(that.renderTimeLine / 1000) % that.renderDelta) - costTime;
      let timeUntilNextTick = Math.max(0, renderTime);
      // console.log("next tick time:", timeUntilNextTick, ", ", renderTime, ", ", Math.ceil(that.renderTimeLine / 1000), ", ", Math.ceil(that.renderTimeLine / 1000) % that.renderDelta, ", ",  costTime, ", ", that.currentFrameNumber);

      setTimeout((frameNumber) => {
        if (frameNumber != that.currentFrameNumber) {
          // console.log("Render Pass:", frameNumber);
          return;
        }

        that.renderTimeLine += (that.getCurrentTime() - that.frameStartTime) * 1000;
        that.renderVideo();
      }, timeUntilNextTick, that.currentFrameNumber);
    }
  }

  prepareAudioPlayer(audioTimelineStr, renderFPS, suc, fail) {
    console.log("[assistant] prepareAudioPlayer");
    let that = this;
    that.audioPlayer.onStatusChanged = (preStatus, curStatus) => {
      if (that.onStatusChanged) {
        that.onStatusChanged(preStatus, curStatus);
      }
    };

    that.renderFPS = renderFPS;
    that.renderDelta = 1000 / renderFPS;

    let audioTimeline = JSON.parse(audioTimelineStr);
    that.renderDuration =audioTimeline.duration_us;
    console.log("audioTimeline:", audioTimeline);
    this.audioPlayer.init(
      audioTimeline,
      () => {
        console.log("[assistant] audio player init suc, ctx state=", this.audioPlayer.audioContext.state);
        suc && suc();
      },
      (err) => {
        console.log("[assistant] audio player init fail: ", err);
        fail && fail(err);
      }
    );
  }

  notifyRenderDataUpdated() {
    let that = this;
    if (that.stopRoll && that.continueRender) {
      // console.log("notifyRenderDataUpdated");
      that.renderTimeLine += (that.getCurrentTime() - that.frameStartTime) * 1000;
      that.renderVideo();
    }
  }

  startRoll(targetTime, stopCondition, stopCallback) {
    let that = this;
    function roll() {
      if (that.stopRoll) {
        return;
      }

      let videoTime = that.renderFunc(targetTime);
      if (videoTime < 0) {
        return;
      }
      // console.log("loop to call runner, return time=", videoTime, " ,targetTime=", targetTime);
      if (stopCondition(videoTime)) {
        that.stopRoll = true;
        if (stopCallback) {
          stopCallback();
        }
      } else {
        setTimeout(() => {roll()}, 1);
      }
    };
    roll();
  }

  terminate(suc, fail) {
    this.audioPlayer.stop(() => {
      this.stopRoll = true;
      this.continueRender = false;
      this.audioPlayer.uninit();
      suc && suc();
    }, fail);
  }

  seekTo(time) {
    console.log("[assistant] seekTo ", time);
    time = Math.max(Math.min(time, this.renderDuration), 0);
    let seekTime = time / 1e6;
    this.audioPlayer.seekTo(seekTime);

    this.renderTimeLine = time;
    if (!this.stopRoll) {
      this.play();
    }
    this.stopRoll = true;
    return this.renderTimeLine;
  }

  play(suc, fail) {
    console.log("[assistant] play");
    this.audioPlayer.resume(() => {
      this.continueRender = true;
      this.renderVideo();
    });
  }

  pause(suc, fail) {
    this.audioPlayer.pause(() => {
      this.stopRoll = true;
      this.continueRender = false;
      suc && suc();
    }, fail);
  }

  duration() {
    return this.audioPlayer.timeline.duration;
  }

  duration_us() {
    return this.renderDuration;
  }
}

if (typeof wx != "undefined") module.exports = AudioPlayerAssistant;
