<template>
  <transition name="grow" mode="out-in">
    <div
      v-show="show"
      :class="{ 'voice-recorder': true, ptt: usePTT, row: !usePTT }"
      ref="vrroot"
    >
      <v-container v-if="!usePTT" fluid fill-height>
        <v-row align="center" class="mt-3">
          <v-col cols="4" align="center">
            <v-btn v-show="state == states.RECORDED" icon @click.stop="redo">
              <v-icon color="white">undo</v-icon>
            </v-btn>
            <v-btn v-show="state == states.INITIAL" icon @click.stop="importAudio">
              <v-icon color="white">$vuetify.icons.audio_import</v-icon>
              <input
              ref="audio_import"
              type="file"
              name="audio_import"
              @change="handleAudioImport($event)"
              accept="audio/*"
              class="d-none"
            />
            </v-btn>
            <v-btn v-show="state == states.IMPORTED" icon @click.stop="previewAudio">
              <v-icon color="white">$vuetify.icons.audio_import_play</v-icon>
            </v-btn>
            <v-btn v-show="state == states.PLAYING" icon @click.stop="pauseAudio">
              <v-icon color="white">pause</v-icon>
            </v-btn>
          </v-col>
          <v-col cols="4" align="center">
            <v-btn
              v-if="state == states.RECORDING"
              id="btn-pause"
              icon
              class="voice-recorder-btn recording"
              @click.stop="pauseRecording"
            >
              <v-icon color="white">stop</v-icon>
            </v-btn>
            <v-btn
              v-else-if="state == states.RECORDED || state == states.IMPORTED || state == states.PLAYING"
              id="btn-send"
              class="voice-recorder-btn recorded"
              icon
              :disabled="!recordedFile"
              @click.stop="send"
            >
              <v-icon color="black">arrow_upward</v-icon>
            </v-btn>
            <v-btn
              v-else
              id="btn-record"
              class="voice-recorder-btn"
              icon
              @click.stop="startRecording"
            >
              <v-icon color="white">fiber_manual_record</v-icon>
            </v-btn>
          </v-col>
          <v-col cols="4" align="center">
            <v-btn id="btn-record-cancel" icon @click.stop="cancelRecording">
              <v-icon color="white">close</v-icon>
            </v-btn>
          </v-col>
        </v-row>
      </v-container>

      <v-container fluid fill-height>
        <v-row align="center">
          <v-col cols="3">
            <div class="recording-time">
              {{ recordingTime }}
            </div>
          </v-col>
          <v-col cols="6" v-if="usePTT">
            <div class="swipe-info">
              &lt;&lt; {{ $t("voice_recorder.swipe_to_cancel") }}
            </div>
          </v-col>
        </v-row>
      </v-container>
      <transition name="fade" mode="out-in">
        <div
          v-if="willCancel"
          class="will-cancel"
        >
          <v-container fluid fill-height>
            <v-row align="center">
              <v-col cols="3">
                <v-icon color="white">delete_outline</v-icon>
              </v-col>
              <v-col cols="6">
                <div class="swipe-info">
                  {{ $t("voice_recorder.release_to_cancel") }}
                </div>
              </v-col>
            </v-row>
          </v-container>
        </div>
      </transition>

      <transition name="fade" mode="out-in">
        <div
          v-if="recordingLocked"
          class="locked"
        >
          <v-container fluid fill-height>
            <v-row align="center">
              <v-col cols="3">
                <div class="recording-time">
                  {{ recordingTime }}
                </div>
              </v-col>
              <v-col cols="3" class="pa-0">
                <v-btn id="btn-record-cancel" @click.stop="cancelRecording" text class="swipe-info">{{
                  $t("menu.cancel")
                }}</v-btn>
              </v-col>
              <v-col cols="3" class="pa-0">
                <v-btn id="btn-record-stop" @click.stop="stopRecording" icon class="swipe-info"
                  ><v-icon color="white">stop</v-icon></v-btn
                >
              </v-col>
            </v-row>
          </v-container>
        </div>
      </transition>

      <div
        v-if="state == states.ERROR"
        class="error"
      >
        <v-container fluid fill-height>
          <v-row align="center">
            <v-col>
              <div class="swipe-info">
                {{ errorMessage || $t("voice_recorder.failed_to_record") }}
              </div>
            </v-col>
            <v-col align="right">
              <v-btn id="btn-record-cancel" icon @click.stop="cancelRecording">
                <v-icon color="white">close</v-icon>
              </v-btn>
            </v-col>
          </v-row>
        </v-container>
      </div>

      <VoiceRecorderLock
        v-show="state == states.RECORDING && usePTT"
        :style="lockButtonStyle"
        :isLocked="recordingLocked"
      />
    </div>
  </transition>
</template>
<script>
const State = {
  INITIAL: "intial",
  RECORDING: "recording",
  RECORDED: "recorded",
  ERROR: "error",
  IMPORTED: "imported",
  PLAYING: "playing"
};
import util from "../plugins/utils";
import VoiceRecorderLock from "./VoiceRecorderLock";
require("md-gum-polyfill");
import MicRecorder from "mic-recorder-to-mp3";
import ysFixWebmDuration from "fix-webm-duration";
//import { duration } from "dayjs";

export default {
  name: "VoiceRecorder",
  components: {
    VoiceRecorderLock,
  },
  props: {
    show: {
      type: Boolean,
      default: function() {
        return false;
      },
    },
    ptt: {
      type: Boolean,
      default: function() {
        return false;
      },
    },
    micButtonRef: {
      type: Object,
      default: function() {
        return null;
      },
    },
  },
  data() {
    return {
      willCancel: false,
      /** Starting X coordinate of dragging operations */
      startCoordinateX: null,
      startCoordinateY: null,
      states: State,
      state: State.INITIAL,
      recordStartedAt: null,
      recordingTime: String.fromCharCode(160), // nbsp!
      recordTimer: null,
      recordingLocked: false,
      recordedFile: null,
      errorMessage: null,
      recorder: null,
      previewPlayer: null,
      wakeLock: null,
      maxRecordingLength: 300, // In seconds
      forceNonPTTMode: false,
    };
  },
  watch: {
    micButtonRef(buttonRef) {
      if (buttonRef) {
        var r = buttonRef.$el.getBoundingClientRect();
        var left = r.left;
        var width = r.right - r.left;
        r = this.$refs.vrroot.parentElement.getBoundingClientRect();
        var widthParent = r.right - r.left;
        document.documentElement.style.setProperty(
          "--v-mic-button-left",
          left + "px"
        );
        document.documentElement.style.setProperty(
          "--v-mic-button-width",
          width + "px"
        );
        document.documentElement.style.setProperty(
          "--v-mic-button-container-width",
          widthParent + "px"
        );
        var initialScale = width / widthParent;
        document.documentElement.style.setProperty(
          "--v-mic-button-initial-scale",
          initialScale
        );
        var initialTranslate = left + width / 2 - widthParent / 2;
        document.documentElement.style.setProperty(
          "--v-mic-button-initial-translate",
          initialTranslate + "px"
        );
      }
    },
    show(val) {
      this.forceNonPTTMode = false;
      if (val) {
        // Add listeners
        this.state = State.INITIAL;
        this.errorMessage = null;
        this.recordedFile = null;
        this.previewPlayer = null;
        this.recordingTime = String.fromCharCode(160);
        if (this.usePTT) {
          document.addEventListener("mouseup", this.mouseUp, false);
          document.addEventListener("mousemove", this.mouseMove, false);
          document.addEventListener("touchend", this.mouseUp, false);
          document.addEventListener("touchmove", this.mouseMove, false);
          this.startRecording();
        } else {
          console.log("Not PTT");
          if (this.micButtonRef) {
          //eslint-disable-next-line
          this.micButtonRef.$el.style.display = "none";
          }
        }
      } else {
        // Remove listeners
        document.removeEventListener("mouseup", this.mouseUp, false);
        document.removeEventListener("mousemove", this.mouseMove, false);
        document.removeEventListener("touchend", this.mouseUp, false);
        document.removeEventListener("touchmove", this.mouseMove, false);
        this.startCoordinateX = null;
        this.startCoordinateY = null;
        this.willCancel = false;
        this.startCoordinateX = null;
        this.startCoordinateY = null;
        this.recordingLocked = false;
        if (this.micButtonRef) {
        //eslint-disable-next-line
        this.micButtonRef.$el.style.display = "block";
        }
      }
    },
    state() {
      if (this.state != State.IMPORTED && this.previewPlayer) {
        this.previewPlayer.pause();
      }
    }
  },
  computed: {
    usePTT() {
      return this.ptt && !this.forceNonPTTMode;
    },
    lockButtonStyle() {
      /**
        Calculate where to show the lock button (it should be at the same X-coord as the)
        mic button (given as a reference!)
      */
      var left = 0;
      var width = 20;
      if (this.micButtonRef) {
        var r = this.micButtonRef.$el.getBoundingClientRect();
        left = r.left;
        width = r.right - r.left;
      }
      const s =
        "position:absolute;top:-50px;left:" +
        left +
        "px;width:" +
        width +
        "px;height:40px";
      return s;
    },
  },
  methods: {
    close() {
      this.stopRecordTimer();
      this.recordingTime = String.fromCharCode(160); // nbsp;
      this.$emit("close");
      this.previewPlayer = null;
      this.recordedFile = null;
      if (this.$refs.audio_import) {
        this.$refs.audio_import.value = null;
      }
    },
    mouseUp(ignoredEvent) {
      document.removeEventListener("mouseup", this.mouseUp, false);
      document.removeEventListener("mousemove", this.mouseMove, false);
      document.removeEventListener("touchend", this.mouseUp, false);
      document.removeEventListener("touchmove", this.mouseMove, false);
      //document.body.style.cursor = "";
      if (this.state == State.RECORDING) {
        if (!this.recordingLocked) {
          if (this.willCancel) {
            this.cancelRecording();
          } else {
            this.stopRecording();
          }
        }
      } else {
        this.cancelRecording();
      }
    },
    mouseMove(event) {
      var x = event.clientX;
      var y = event.clientY;
      if (event.touches && event.touches.length > 0) {
        x = event.touches[0].clientX;
        y = event.touches[0].clientY;
      }
      if (!this.startCoordinateX) {
        // First move, set it!
        this.startCoordinateX = x;
        this.startCoordinateY = y;
      }
      //document.body.style.cursor = "ns-resize";
      this.willCancel = x < this.startCoordinateX - 30;
      if (y < this.startCoordinateY - 30 && !this.willCancel) {
        this.recordingLocked = true;
      }
      event.preventDefault();
      event.stopPropagation();
    },

    startRecording() {
      // Start recording. Browser will request permission to use your microphone.
      this.recorder = new MicRecorder({
        bitRate: 128,
      });
      this.recorder
        .start()
        .then(() => {
          this.state = State.RECORDING;
          this.recordStartedAt = Date.now();
          this.startRecordTimer();
        })
        .then(async () => {
          this.aquireWakeLock();
        })
        .catch((e) => {
          console.error(e);
          if (e && e.name == "NotAllowedError") {
            this.errorMessage = e.message;
          }
          this.state = State.ERROR;
        });
    },
    screenLocked() {
      if (document.visibilityState === "hidden" && this.state == State.RECORDING) {
        this.pauseRecording();
      }
    },
    playRecordedSound() {
      const audio = new Audio(require("@/assets/sounds/record_stop.mp3"));
      audio.play();
    },
    aquireWakeLock() {
      document.addEventListener("visibilitychange", this.screenLocked);
      try {
        if (navigator.wakeLock && !this.wakeLock) {
          navigator.wakeLock.request('screen').then((lock) => this.wakeLock = lock);
        }
      }
      catch(err) { console.error(err)}
    },
    releaseWakeLock() {
      document.removeEventListener("visibilitychange", this.screenLocked);
      if (this.wakeLock) {
        this.wakeLock.release().then(() => {
          this.wakeLock = null;
        });
      }
    },
    cancelRecording() {
      if(this.recorder) {
        this.recorder.stop();
        this.recorder = null;
      }
      this.releaseWakeLock();
      this.state = State.INITIAL;
      this.close();
    },
    pauseRecording() {
      // Remove PTT mode. We can get here in PTT if screen is locked or if max time is reached.
      if (this.ptt) {
        this.forceNonPTTMode = true;
        this.recordingLocked = false;
        document.removeEventListener("mouseup", this.mouseUp, false);
        document.removeEventListener("mousemove", this.mouseMove, false);
        document.removeEventListener("touchend", this.mouseUp, false);
        document.removeEventListener("touchmove", this.mouseMove, false);
      }
      this.state = State.RECORDED;
      this.stopRecordTimer();
      this.releaseWakeLock();
      this.getFile(false);
      this.playRecordedSound();
    },
    stopRecording() {
      this.state = State.RECORDED;
      this.stopRecordTimer();
      this.releaseWakeLock();
      this.recordingTime = String.fromCharCode(160); // nbsp;
      this.close();
      this.getFile(true);
      this.playRecordedSound();
    },
    redo() {
      this.state = State.INITIAL;
      this.recordedFile = null;
      this.recordingTime = String.fromCharCode(160); // nbsp;
    },
    send() {
      this.$emit("file", { file: this.recordedFile });
      if (this.previewPlayer) {
        this.previewPlayer.pause();
      }
      if (this.$refs.audio_import) {
        this.$refs.audio_import.value = null;
      }
    },
    getFile(send) {
      const duration = Date.now() - this.recordStartedAt;
      this.recorder
        .stop()
        .getMp3()
        .then(([buffer, blob]) => {
          // do what ever you want with buffer and blob
          // Example: Create a mp3 file and play
          this.recordedFile = new File(
            buffer,
            util.formatRecordStartTime(this.recordStartedAt) + ".mp3",
            {
              type: blob.type,
              lastModified: Date.now(),
            }
          );
          this.recordedFile.duration = duration;
          if (send) {
            this.send();
          }
        });
    },
    startRecordTimer() {
      this.stopRecordTimer();
      this.recordingTime = String.fromCharCode(160); // nbsp;
      this.recordTimer = setInterval(() => {
        const now = Date.now();
        this.recordingTime = util.formatRecordDuration(
          now - this.recordStartedAt
        );
        // Auto-stop?
        if ((now - this.recordStartedAt) >= (1000 * this.maxRecordingLength) && this.state == State.RECORDING) {
          this.pauseRecording();
        }
      }, 500);
    },
    stopRecordTimer() {
      if (this.recordTimer) {
        clearInterval(this.recordTimer);
        this.recordTimer = null;
      }
    },

    /*
     * There is an issue with browsers not setting correct metadata in the generated webm file.
     * See here: https://bugs.chromium.org/p/chromium/issues/detail?id=642012
     * Use fix-webm-duration package to try to update the cues section.
     */
    async correctMetadata(blob, duration) {
      return new Promise((resolve, reject) => {
        try {
          ysFixWebmDuration(blob, duration, function(fixedBlob) {
            const b = new Blob([fixedBlob], { type: blob.type });
            resolve(b);
          });
        } catch (err) {
          console.error(err);
          reject(err);
        }
      });
    },

    /**
     * Show import picker to select file
     */
     importAudio() {
      this.$refs.audio_import.click();
    },

    /**
     * Handle picked audio file
     */
    handleAudioImport(event) {
      if (event.target.files && event.target.files[0]) {
        this.recordedFile = event.target.files[0];
        this.state = State.IMPORTED;
      }
    },

    previewAudio() {
      this.state = State.PLAYING;
      if (this.recordedFile) {
        if (!this.previewPlayer) {
          this.previewPlayer = new Audio();
        }
        var reader = new FileReader();
        reader.onload = (e) => {
        this.previewPlayer.src = e.target.result;
        this.previewPlayer.play();
        };
        reader.readAsDataURL(this.recordedFile);
      }
    },
    pauseAudio() {
      this.state = State.IMPORTED;
      this.previewPlayer.pause();
    }
  },
};
</script>

<style lang="scss">
  @import "@/assets/css/chat.scss";
  @import "@/assets/css/components/voice-recorder.scss";
</style>
