import Hls from 'hls.js';
import {
  FrontSide,
  Mesh,
  MeshBasicMaterial,
  PlaneGeometry,
  VideoTexture,
} from 'three';
import { clamp } from 'three/src/math/MathUtils';

export class VideoPlayer {
  public video = document.createElement('video');
  public material!: MeshBasicMaterial;
  public texture!: VideoTexture;
  public mesh!: Mesh;
  public hls?: Hls;
  public playable: boolean;
  public geometry!: PlaneGeometry;

  constructor(public width: number, public height: number) {}

  initialize() {
    this.texture = new VideoTexture(this.video);
    this.material = new MeshBasicMaterial({
      map: this.texture,
      side: FrontSide,
      toneMapped: false,
    });
    this.geometry = new PlaneGeometry(this.width, this.height);
    this.mesh = new Mesh(this.geometry, this.material);

    this.video.addEventListener('canplay', () => {
      this.playable = true;
    });

    this.video.addEventListener('error', () => {
      this.playable = false;
      this.video.pause();
      this.video.src = undefined;
    });

    this.video.volume = 0.8;
  }

  public setSource(source: string, autoplay = false) {
    this.video.pause();
    if (this.hls) {
      this.hls.stopLoad();
      this.hls.destroy();
    }

    if (source.endsWith('m3u8')) {
      if (Hls.isSupported()) {
        this.hls = new Hls();
        this.hls.loadSource(source);
        this.hls.attachMedia(this.video);

        this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
          this.playable = true;

          if (autoplay) {
            this.video.play();
          } else {
            this.hls.stopLoad();
          }
        });

        this.hls.on(Hls.Events.ERROR, (e, d) => {
          if (d.fatal) {
            this.playable = false;
            this.video.pause();
            this.hls.stopLoad();
          }
        });
        return;
      }
    }
    this.video.src = source;

    if (autoplay) {
      this.video.play();
    }
  }

  public play() {
    if (this.playable) {
      if (this.hls) {
        this.hls.startLoad(-1);
      }
      this.video.play();
    }
  }

  public stop() {
    this.video.pause();
    if (this.hls) {
      this.hls.stopLoad();
    }
  }

  public setVolume(percent: number) {
    const setter = clamp(percent, 0, 100) / 100;
    this.video.volume = setter;
  }
}
