<template>
  <el-dialog
    class="audio-record-dialog"
    :visible.sync="dialogVisible"
    width="390px"
    title="语音提醒"
    :close-on-press-escape="false"
    :close-on-click-modal="false"
    append-to-body
    @open="initData"
  >
    <div class="record-time" v-if="recordStep !== 2">
      <p>{{ recordTimeStr }}</p>
      <p>(最长2分钟)</p>
    </div>
    <div class="record-audio-play" v-else>
      <div class="audio-player">
        <div
          :class="['audio-player-play', { 'player-playing': isPlaying }]"
          @click="play"
        ></div>
        <div class="audio-player-progress">
          <el-progress
            :percentage="audioPlayProgress"
            :show-text="false"
            :stroke-width="8"
            status="success"
          ></el-progress>
          <p class="audio-player-tips">点击试听录音</p>
        </div>
        <div class="audio-player-time">{{ audioCurPlayTime }}</div>
      </div>
      <audio
        :src="audioUrl"
        class="audio-audio"
        ref="AudioAudio"
        @timeupdate="audioCurrentTimeChange"
        @ended="audioEnded"
      ></audio>
    </div>
    <div class="record-footer" slot="footer">
      <el-button type="primary" v-if="recordStep === 0" @click="start"
        >开始录音</el-button
      >
      <el-button type="primary" v-if="recordStep === 1" @click="finish"
        >完成录音</el-button
      >

      <el-button v-if="recordStep === 2" @click="restart">重新录音</el-button>
      <el-button
        type="primary"
        v-if="recordStep === 2"
        @click="confirm"
        :disabled="isSubmit"
        >确认录音</el-button
      >
    </div>
  </el-dialog>
</template>

<script>
import MD5 from "js-md5";
import { uploadFile } from "@/api/system-info";
import { sendWarningMsg } from "@/api/invigilation";
import AudioRecord from "./audioRecord";

function recordTimeToText(timeNumber) {
  const MINUTE_TIME = 60 * 1000;
  const SECOND_TIME = 1000;
  let [minute, second] = [0, 0];
  let residueTime = timeNumber;

  if (residueTime >= MINUTE_TIME) {
    minute = Math.floor(residueTime / MINUTE_TIME);
    residueTime -= minute * MINUTE_TIME;
  }
  if (residueTime >= SECOND_TIME) {
    second = Math.round(residueTime / SECOND_TIME);
  }

  return [minute, second]
    .map((item) => ("00" + item).substr(("" + item).length))
    .join(":");
}

export default {
  name: "audio-record-dialog",
  props: {
    recordId: {
      type: String,
    },
  },
  data() {
    return {
      dialogVisible: false,
      isSubmit: false,
      recordStep: 0, // 0:未开始，1：开始录音，2：录音结束
      recordReady: false,
      recordLimitTime: 2 * 60 * 1000,
      recordStartTime: 0,
      recordEndTime: 0,
      recordTime: 0,
      recordTimeStr: "00:00",
      audioUrl: "",
      isPlaying: false,
      audioPlayProgress: 0,
      audioCurPlayTime: "00:00",
      audioRecord: null,
      audioBlob: null,
      setT: null,
    };
  },
  methods: {
    initData() {
      this.isSubmit = false;
      this.recordStep = 0;
      this.recordStartTime = 0;
      this.recordEndTime = 0;
      this.recordTime = 0;
      this.recordTimeStr = "00:00";
      this.audioUrl = "";
      this.audioPlayProgress = 0;
      this.audioCurPlayTime = "00:00";
      this.audioBlob = null;
      this.setT = null;
    },
    cancel() {
      this.dialogVisible = false;
    },
    open() {
      this.dialogVisible = true;
    },
    // audio play
    play() {
      if (this.$refs.AudioAudio.ended) {
        this.$refs.AudioAudio.currentTime = 0;
        this.$refs.AudioAudio.play();
        this.isPlaying = true;
        this.audioPlayProgress = 0;
        this.audioCurPlayTime = "00:00";
        return;
      }

      if (this.$refs.AudioAudio.paused) {
        this.$refs.AudioAudio.play();
        this.isPlaying = true;
      } else {
        this.$refs.AudioAudio.pause();
        this.isPlaying = false;
      }
    },
    audioCurrentTimeChange() {
      const { duration, currentTime } = this.$refs.AudioAudio;
      const audioPlayProgress = (100 * currentTime) / duration;
      this.audioPlayProgress = audioPlayProgress
        ? audioPlayProgress > 100
          ? 100
          : audioPlayProgress
        : 0;
      this.audioCurPlayTime = recordTimeToText(currentTime * 1000);
    },
    audioEnded() {
      this.isPlaying = false;
    },
    // audio record
    updateRecordTime() {
      this.recordTime = Date.now() - this.recordStartTime;
      this.recordTimeStr = recordTimeToText(this.recordTime);
    },
    start() {
      this.audioRecord = new AudioRecord({
        onaudioprocess: this.updateRecordTime,
      });
      this.audioRecord.init((err) => {
        if (err) {
          this.recordReady = false;
          this.$message.error({ message: err.msg, duration: 3000 });
          return;
        }
        this.recordStartTime = Date.now();
        this.audioRecord.start();
        this.recordStep = 1;
        this.setT = setTimeout(() => {
          this.finish();
        }, this.recordLimitTime);
      });
    },
    finish() {
      if (this.setT) clearTimeout(this.setT);

      this.audioBlob = this.audioRecord.getAudioBlob();
      console.log(this.audioBlob);
      this.audioUrl = window.URL.createObjectURL(this.audioBlob);
      this.updateRecordTime();
      this.recordStep = 2;
      this.audioRecord.recover();
      console.log(this.recordTime);
      console.log(this.recordTimeStr);
    },
    restart() {
      this.initData();
      this.start();
    },
    async confirm() {
      if (this.isSubmit) return;
      this.isSubmit = true;

      async function blobToArray(blob) {
        return new Promise((resolve) => {
          var reader = new FileReader();
          reader.addEventListener("loadend", function () {
            resolve(reader.result);
          });
          reader.readAsArrayBuffer(blob);
        });
      }
      const ab = await blobToArray(this.audioBlob);
      const md5 = MD5(ab);
      const res = await uploadFile({
        file: this.audioBlob,
        md5,
      }).catch(() => {});

      if (!res) {
        this.isSubmit = false;
        return;
      }

      const result = await sendWarningMsg({
        content: res.data.data.url,
        type: "audio",
        examRecordId: this.recordId,
      }).catch(() => {});

      this.isSubmit = false;
      if (!result) return;
      this.$emit("modified");
      this.cancel();
    },
  },
  beforeDestroy() {},
};

// 参考文件：
// https://blog.csdn.net/qq_42986378/article/details/81872679
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
// https://developer.mozilla.org/zh-CN/docs/Web/API/AudioContext
// https://developer.mozilla.org/zh-CN/docs/Web/API/AudioContext/createMediaStreamSource
// https://developer.mozilla.org/zh-CN/docs/Web/API/AudioContext/createScriptProcessor
// https://www.cnblogs.com/hustskyking/p/javascript-array.html
// https://www.barretlee.com/blog/2014/02/20/cb-webAudio-listen/
// https://www.barretlee.com/blog/2014/02/22/cb-webAudio-show-audio/

// method
// navigator.mediaDevices.getUserMedia => stream
// new AudioContext() => context
// context.createMediaStreamSource(stream) => audioInput
// context.createScriptProcessor(4096, 1, 1) => recorder

// start => () => {
//   audioInput.connect(recorder);
//   recorder.connect(context.destination);
// }

// recording
// recorder.onaudioprocess => () => {audioData.push(e.inputBuffer.getChannelData(0))}

// stop => () => {
//   recorder.disconnect()
// }
</script>

<style lang="scss">
.record-time {
  padding: 30px;
  text-align: center;
  > p:first-child {
    font-size: 40px;
    line-height: 1.1;
    font-weight: 400;
    color: #202b4b;
    margin: 0;
  }
  > p:last-child {
    font-size: 14px;
    font-weight: 400;
    color: #8c94ac;
    line-height: 20px;
    margin: 0;
  }
}
.record-audio-play {
  padding: 30px 0;

  .audio-audio {
    visibility: hidden;
  }
}
.audio-player {
  height: 64px;
  padding: 16px 25px;
  background: #f0f4f9;
  border-radius: 10px;
  position: relative;

  &-play {
    width: 32px;
    height: 32px;
    float: left;
    background-image: url("../../../../assets/icon-play.png");
    background-size: 100% 100%;
    cursor: pointer;

    &.player-playing {
      background-image: url("../../../../assets/icon-play-act.png");
    }
    &:hover {
      background-image: url("../../../../assets/icon-play-act.png");
    }
  }
  &-progress {
    margin-left: 40px;
    margin-right: 40px;
    padding-top: 12px;
    position: relative;
  }
  &-tips {
    position: absolute;
    left: 0;
    top: 24px;
    font-size: 10px;
    font-weight: 400;
    color: #8c94ac;
    line-height: 14px;
  }
  &-time {
    position: absolute;
    right: 25px;
    top: 23px;
    height: 18px;
    font-size: 12px;
    font-weight: 400;
    color: #202b4b;
    line-height: 18px;
  }
  .el-progress-bar__outer {
    background: #dbe1e7;
  }
  .el-progress.is-success .el-progress-bar__inner {
    background: #1cd1a1;
  }
}
.record-footer {
  text-align: center;
}
</style>
