<script setup>
import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef, watchEffect } from "vue";
import { useDevicesList, useUserMedia } from "@vueuse/core";
import { getBase64Strings } from "exif-rotate-js";
import { useAlert } from "../../../composables/alert";
import { VInput } from "vuetify/components/VInput";
const props = defineProps({
  imageFormat: { type: String, required: false, default: "image/jpeg" },
  autoStart: { type: Boolean, required: false, default: true },
  fileOnly: { type: Boolean, required: false, default: false },
  readonly: { type: Boolean, required: false, default: false },
  disabled: { type: Boolean, required: false, default: false },
  buttonText: { type: String, required: false, default: "Add Photo" },
  maxHeight: { type: [String, Number], required: false },
  maxWidth: { type: [String, Number], required: false, default: 1024 },
  aspectRatio: { type: Number, required: false }
});
const alert = useAlert();
const imageData = defineModel({ type: String });
const inputRef = useTemplateRef("inputRef");
const isLoading = ref(false);
const isCaptured = computed(() => !!imageData.value);
const isEditing = ref(false);
const videoScreen = ref();
const currentCameraId = ref();
const { videoInputs: cameras } = useDevicesList({
  requestPermissions: true,
  constraints: { audio: false, video: true },
  onUpdated() {
    if (!cameras.value.find((camera) => camera.deviceId === currentCameraId.value))
      currentCameraId.value = cameras.value[0]?.deviceId;
  }
});
const hasCamera = computed(() => {
  return !!currentCameraId.value;
});
const { stream, start: cameraStart, stop: cameraStop, enabled: cameraEnabled } = useUserMedia({
  constraints: { video: { deviceId: currentCameraId.value, width: { min: 1280 }, aspectRatio: props.aspectRatio } }
});
watchEffect(() => {
  if (videoScreen.value) videoScreen.value.srcObject = stream.value ? stream.value : null;
});
function startCamera() {
  imageData.value = void 0;
  if (!cameraEnabled.value) {
    isLoading.value = true;
    cameraStart().finally(() => {
      isLoading.value = false;
    });
  }
}
function stopCamera() {
  if (cameraEnabled.value) cameraStop();
}
function captureImage() {
  if (videoScreen.value) {
    const canvas = document.createElement("canvas");
    canvas.width = videoScreen.value.videoWidth;
    canvas.height = videoScreen.value.videoHeight;
    const context = canvas.getContext("2d");
    if (context) {
      context.drawImage(videoScreen.value, 0, 0, canvas.width, canvas.height);
      isEditing.value = false;
      imageData.value = canvas.toDataURL(props.imageFormat);
      stopCamera();
    }
  }
}
function captureImageFile(selectedFile) {
  if (!selectedFile) {
    alert?.addAlert({ message: "No file selected.", alertType: "error" });
    return;
  }
  const scanImageSingleFile = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile;
  getBase64Strings([scanImageSingleFile], { maxSize: computedMaxSize.value, type: props.imageFormat, quality: 1 }).then((returnData) => {
    isEditing.value = false;
    imageData.value = returnData[0];
    stopCamera();
  }).catch((e) => void e);
}
onMounted(() => {
  if (!isCaptured.value && props.autoStart && !props.fileOnly) startCamera();
});
onBeforeUnmount(() => {
  stopCamera();
});
function reset() {
  inputRef.value?.reset();
  stopCamera();
  if (props.autoStart && !props.fileOnly) startCamera();
}
const computedMaxWidth = computed(() => {
  if (typeof props.maxWidth === "number") {
    return `${props.maxWidth}px`;
  } else if (!isNaN(Number(props.maxWidth))) {
    return `${props.maxWidth}px`;
  }
  return props.maxWidth;
});
const computedMaxHeight = computed(() => {
  if (typeof props.maxHeight === "number") {
    return `${props.maxHeight}px`;
  } else if (!isNaN(Number(props.maxHeight))) {
    return `${props.maxHeight}px`;
  }
  return props.maxHeight;
});
const computedMaxSize = computed(() => {
  let tmpMaxHeight = 1024;
  let tmpMaxWidth = 1024;
  if (typeof props.maxWidth === "number") {
    tmpMaxWidth = props.maxWidth;
  } else if (!isNaN(Number(props.maxWidth))) {
    tmpMaxWidth = Number(props.maxWidth);
  }
  if (typeof props.maxHeight === "number") {
    tmpMaxHeight = props.maxHeight;
  } else if (!isNaN(Number(props.maxHeight))) {
    tmpMaxHeight = Number(props.maxHeight);
  }
  return tmpMaxWidth > tmpMaxHeight ? tmpMaxWidth : tmpMaxHeight;
});
const operation = ref({ startCamera, stopCamera, reset, captureImage, captureImageFile, isLoading, isCaptured, hasCamera });
const isValid = computed(() => {
  return inputRef.value?.isValid;
});
const errorMessages = computed(() => {
  return inputRef.value?.errorMessages;
});
defineExpose({
  errorMessages,
  isValid,
  reset,
  resetValidation: () => inputRef.value?.resetValidation(),
  validate: () => inputRef.value?.validate(),
  operation
});
</script>

<template>
  <v-input v-model="imageData" v-bind="$attrs" ref="inputRef">
    <template #default="{ isReadonly, isDisabled }">
      <v-container fluid class="ma-0 pa-0">
        <v-card>
          <v-card-text class="d-flex justify-center text-center" v-if="!isLoading && !isCaptured">
            <template v-if="!hasCamera || fileOnly">
              <FileBtn
                  color="primary"
                  variant="flat"
                  accept="image/*"
                  @update:model-value="captureImageFile"
                  :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
              >
                <v-icon>mdi mdi-image-plus</v-icon>
                {{ buttonText }}
              </FileBtn>
            </template>
            <template v-else>
              <div style="position: relative; display: inline-block; width: 100%;" :style="{ maxWidth: computedMaxWidth, maxHeight: computedMaxHeight }">
                <video autoplay ref="videoScreen" width="100%" :style="{ maxWidth: computedMaxWidth, maxHeight: computedMaxHeight }"></video>
                <div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
                  <FileBtn
                      accept="image/*"
                      icon="mdi mdi-image-plus"
                      icon-only
                      @update:model-value="captureImageFile"
                      :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
                  />
                </div>
              </div>
            </template>
          </v-card-text>
          <v-card-text class="d-flex justify-center" v-if="isCaptured">
            <div style="position: relative; display: inline-block; width: 100%;" :style="{ maxWidth: computedMaxWidth, maxHeight: computedMaxHeight }" v-if="!isEditing">
              <v-img :src="imageData" :max-height="maxHeight" :max-width="maxWidth" contain></v-img>
              <div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
                <v-btn
                    icon="mdi mdi-image-edit"
                    icon-only
                    @click="isEditing = true"
                    :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
                />
              </div>
            </div>
            <form-images-edit v-model="imageData" :aspect-ratio="aspectRatio" :image-format="imageFormat" @update:model-value="isEditing = false" @close="isEditing = false"  v-else></form-images-edit>
          </v-card-text>
          <v-card-text class="d-flex justify-center" v-if="isLoading">
            <v-progress-circular indeterminate></v-progress-circular>
          </v-card-text>
          <v-card-actions v-if="!readonly && (!fileOnly || isCaptured) && !isEditing && !(isReadonly.value || isDisabled.value)">
            <slot name="actions" :operation="operation">
              <v-spacer></v-spacer>
              <v-btn color="primary" variant="flat" @click="startCamera" v-if="!cameraEnabled && hasCamera && !fileOnly" :disabled="disabled">{{ isCaptured ? "Retake" : "Start" }}</v-btn>
              <v-btn color="primary" variant="flat" @click="captureImage" v-if="cameraEnabled" :disabled="disabled">Capture</v-btn>
              <v-btn color="primary" variant="flat" @click="reset" :disabled="disabled">Reset</v-btn>
              <v-spacer></v-spacer>
            </slot>
          </v-card-actions>
        </v-card>
      </v-container>
    </template>
  </v-input>
</template>
