<template>
  <div :class="['chat--input', uiState]">
    <div class="chat--input-content">
      <!-- File input -->

      <!-- Text input -->
      <div v-if="uiState == UiState.Text" class="chat--input-text">
        <input
          class="chat--input-text-input"
          type="text"
          placeholder="Write something..."
          v-model="text"
          v-on:keyup.enter="sendMessage"
        />
        <InputFilePicker v-if="!isTyping" v-on:complete="pickClose" v-on:message="emitMessage" />
        <!-- <a
          class="chat--input-text-attach"
          v-if="uiState == UiState.Text"
          v-on:click="pickFile()"
        ></a>-->
      </div>

      <!-- Recording info -->
      <div v-if="uiState == UiState.Recording" class="chat--input-recording-info">
        <span class="recording-symbol">•</span>
        <span class="recording-duration">{{ recordedDurationString }}</span>
        <span class="recording-cancel">← Swipe to cancel</span>
      </div>

      <!-- Big multi-purpose button -->
      <a v-if="uiState != UiState.File" :class="bigButtonClass" v-on:click="handleBigButton()"></a>
      <!-- v-on:mouseleave="endRecord()"
      v-on:click="endRecord()"-->
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
import { Message, InputMessage, Attachment } from "./type";
import MessageText from "./MessageText.vue";
import InputFilePicker from "./InputFilePicker.vue";
// @ts-ignore
import RecorderService from "../services/recorder";

interface UiStateInf {
  Text: string;
  Recording: string;
  File: string;
}

@Component({
  components: {
    InputFilePicker
  }
})
export default class Input extends Vue {
  // Defined UI State
  private UiState: UiStateInf = {
    Text: "Text",
    Recording: "Recording",
    File: "File"
  };

  private text: string = "";
  private recorder: any = null; // TS fails on 'Recorder'
  private recording = false;
  private uiState: string = this.UiState.Text;

  private recordMonitorInterval: any;
  private recordingTimeStart: number = 0;
  private recordedDurationString: string = "0:00";

  get isTyping() {
    return this.text.length > 0;
  }

  get bigButtonClass() {
    let clazz = "chat--input-record";

    if (this.isTyping) {
      clazz += " user-typing";
    } else if (!this.recorder) {
      clazz += " user-typing disabled";
    }

    return clazz;
  }

  public handleBigButton() {
    if (this.isTyping) {
      // Acts as a send button
      this.sendMessage();
    } else {
      // recorder will be null if it is disabled
      if (!this.recorder) return;
      // Acts as a record button
      this.toggleRecord();
    }
  }

  public setState(state: string) {
    console.info("[Input] stage change to " + state);
    this.uiState = state;
  }

  private sendMessage() {
    const content = this.text.trim();
    if (content.length > 0) {
      const message = InputMessage.newTextMessage(this.text);
      this.$emit("new_message", message);
      this.text = "";
    }
  }

  private sendAudioMessage(data: any) {
    if (data == null) {
      return;
    }

    const attachement = Attachment.fromBlob(
      data.blob,
      data.mimeType,
      "",
      false
    );

    const message = InputMessage.newAudioMessage(attachement);
    this.$emit("new_message", message);
    this.text = "";
  }

  private emitMessage(message: Message) {
    this.$emit("new_message", message);
  }

  created() {
    // we will disable recording on PWA on iOS devices
    const iOS =
      !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
    if (iOS) {
      if (navigator.standalone === true) {
        return;
      }
    }

    console.log("Initialize new recorder service");
    // initialize new recorder 1 time
    this.recorder = new RecorderService(location.origin + "/recorder");
    this.recorder.em.addEventListener("recording", this.whenRecordingAvailable);

    // Force browser to use manual encoding, mp3
    this.recorder.config.usingMediaRecorder = false;
    this.recorder.config.manualEncoderId = "mp3";
    // fix echo issue
    this.recorder.config.enableEchoCancellation = false;
    // set micGain to 1.5x if the device is android
    if (/(android)/i.test(navigator.userAgent)) {
      this.recorder.config.micGain = 1.5;
    }
  }

  private startRecording() {
    if (!this.recorder) return;

    console.log("[Input] Starting recorder");

    this.recorder
      .startRecording()
      .then(() => {
        console.log("[Input] Recording worker started");
        this.setState(this.UiState.Recording);
        // save current timestamp for recording duration
        this.recordingTimeStart = Date.now();
        this.recordMonitorInterval = setInterval(
          this.setRecordingDuration,
          1000
        );
      })
      .catch((error: Error) => {
        console.error("Exception while start recording: " + error);
      });
  }

  private whenRecordingAvailable(evt: CustomEvent) {
    /**
     * @typedef {Object} Recording
     * @property {String} Recording.blob
     * @property {String} Recording.mimeType
     * @property {number} Recording.size
     * @property {number} Recording.ts
     */

    this.sendAudioMessage(evt.detail.recording);
  }

  private setRecordingDuration() {
    if (this.recordingTimeStart <= 0) {
      this.recordedDurationString = "0:00";
    } else {
      const duration = Math.floor(
        (Date.now() - this.recordingTimeStart) / 1000
      );
      // pretty string
      const minutes = Math.floor(duration / 60);
      const seconds = duration - minutes * 60;
      this.recordedDurationString =
        minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
    }
  }

  private toggleRecord() {
    this.recording ? this.endRecord() : this.beginRecord();
  }

  // End audio recording
  private endRecord() {
    if (!this.recording) return;

    console.log("[Input] end recording requested");

    this.recorder.stopRecording();
    this.recording = false;

    clearInterval(this.recordMonitorInterval);
    this.recordMonitorInterval = null;
    this.recordedDurationString = "0:00";
    this.recordingTimeStart = 0;

    this.setState(this.UiState.Text);
  }

  /**
   * Begin recording audio
   */
  private beginRecord() {
    this.recording = true;
    console.log("[Input] begin recording requested");

    this.startRecording();
  }

  // Start Picking a file
  private pickFile() {
    this.setState(this.UiState.File);
  }

  private pickClose() {
    this.setState(this.UiState.Text);
  }

  private beforeDestroy() {
    // cleanup audio resource
    this.endRecord();

    if (!this.recorder) return;

    this.recorder.em.removeEventListener(
      "recording",
      this.whenRecordingAvailable
    );
    this.recorder = null;
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
