import { action, computed, makeObservable, observable } from "mobx";
import AudioSource from "./AudioSource";
// import ImpulseResponseAudio from "../samples/impulse-responses/echohall.wav";

export default class Player extends AudioSource {
  audioBuffer: AudioBuffer | null;
  playing: boolean;
  progress: number;
  startTime: number;
  blob?: Blob;

  constructor() {
    super();

    this.playing = false;
    this.audioBuffer = null;
    this.progress = 0;
    this.startTime = 0;
    this.blob = undefined;

    makeObservable(this, {
      audioBuffer: observable,
      currentProgress: computed,
      currentTime: computed,
      duration: computed,
      hasAudioBuffer: computed,
      isPlaying: computed,
      loadMedia: action,
      play: action,
      playing: observable,
      progress: observable,
      startTime: observable,
      blob: observable,
      startTimer: action,
      stop: action,
      togglePlayPause: action,
      setBlob: action,
    });
  }

  get isPlaying() {
    return this.playing === true;
  }

  get hasAudioBuffer() {
    return this.audioBuffer !== null;
  }

  get duration() {
    return this.audioBuffer?.duration || 0;
  }

  get currentTime() {
    return this.progress;
  }

  get currentProgress() {
    return (100 * this.progress) / (this.duration || 1);
  }

  setBlob = (blob: Blob) => {
    this.blob = blob;
  };

  loadMedia = async (filepath: string) => {
    this.initContext();
    this.audioBuffer = await this.initWithMediaFilepath(filepath);
    return true;
  };

  togglePlayPause = (reverb?: boolean) => {
    if (this.audioBuffer !== null) {
      if (this.isSuspended && this.audioContext) {
        this.audioContext.resume();
      }
      // play or pause track depending on state
      if (this.isPlaying === false) {
        if (reverb) {
          this.playWithReverb();
        } else {
          this.play();
        }
      } else {
        this.stop();
      }
    } else {
      console.warn("Must have an audio buffer to play");
    }
  };

  stop = () => {
    if (this.source) {
      this.progress = 0;
      if (this.audioBuffer && this.audioContext) {
        if (this.playing === true) {
          this.source.stop();
          this.playing = false;
        }
      }
    }
  };

  play = () => {
    if (this.audioBuffer && this.audioContext) {
      this.source = this.audioContext.createBufferSource();
      this.source.buffer = this.audioBuffer;
      this.source
        .connect(this.baseEq)
        .connect(this.midEq)
        .connect(this.trebleEq)
        .connect(this.gainNode)
        .connect(this.analyzerNode)
        .connect(this.audioContext.destination);

      this.source.start(0);
      this.playing = true;
      this.startTimer();
      this.source.onended = () => {
        this.stop();
      };
      return this.source;
    }
    return null;
  };

  playWithReverb = async () => {
    if (this.audioBuffer && this.audioContext) {
      this.source = this.audioContext.createBufferSource();
      this.source.buffer = this.audioBuffer;

      let convolver = this.audioContext.createConvolver();
      // convolver.buffer = await this.audioContext.decodeAudioData(
      //   await (await fetch(ImpulseResponseAudio)).arrayBuffer(),
      // );

      this.source
        .connect(convolver)
        .connect(this.gainNode)
        .connect(this.audioContext.destination);

      this.source.start(0);
      this.playing = true;
      this.startTimer();
      this.source.onended = () => {
        this.stop();
      };
    }
  };

  startTimer = () => {
    if (this.startTime === 0) {
      if (this.audioContext) {
        this.startTime = this.audioContext.currentTime;
      }
    }
    if (this.isPlaying) {
      if (this.audioContext) {
        this.progress =
          this.audioContext.currentTime - this.startTime;
      }
      requestAnimationFrame(this.startTimer);
    } else {
      this.startTime = 0;
      this.progress = 0;
    }
  };
}
