<script setup>

import { useTemplateRef, ref, watchEffect } from 'vue';
import { Button } from '@cloudron/pankow';

const fileInput = useTemplateRef('fileInput');

const props = defineProps({
  // in simple mode, you get just an upload button. useful for dialogs which do not save/unset immediately
  mode: { type: String, default: 'editable', required: true },
  src: { type: String, required: true },
  fallbackSrc: { type: String, required: true },
  size: { type: Number, required: false, default: 512 },
  maxSize: { type: Number, required: false, default: 0 },
  displayHeight: { type: String, required: false },
  displayWidth: { type: String, required: false },
  disabled: { type: Boolean, required: false },
  saveHandler: { type: Function, required: false },
  unsetHandler: { type: Function, required: false },
});

const emit = defineEmits(['changed']);

const image = useTemplateRef('image');
const internalSrc = ref('');
const isChanged = ref(false);
const busy = ref(false);

function reset() {
  isChanged.value = false;
  internalSrc.value = props.src;
}

defineExpose({ reset });

watchEffect(() => {
  internalSrc.value = props.src;
  isChanged.value = false;
});

function dataURLtoFile(dataURL, filename) {
  // Split the data URL to get the MIME type and the base64 data
  const [metadata, base64Data] = dataURL.split(',');

  // Extract the MIME type from the metadata
  const mimeType = metadata.match(/:(.*?);/)[1];

  // Decode the base64 data to binary
  const binaryData = atob(base64Data);

  // Create an array buffer from the binary data
  const arrayBuffer = new ArrayBuffer(binaryData.length);
  const view = new Uint8Array(arrayBuffer);
  for (let i = 0; i < binaryData.length; i++) {
    view[i] = binaryData.charCodeAt(i);
  }

  // Create a Blob from the array buffer
  const blob = new Blob([arrayBuffer], { type: mimeType });

  // Create a File object from the Blob
  const file = new File([blob], filename, { type: mimeType });

  return file;
}

async function onSave() {
  if (typeof props.saveHandler !== 'function') return;

  busy.value = true;

  const error = await props.saveHandler(dataURLtoFile(internalSrc.value, 'image.png'));
  if (!error) isChanged.value = false;

  busy.value = false;
}

function onCancel() {
  internalSrc.value = props.src || props.fallbackSrc;
  isChanged.value = false;
  emit('changed', 'src');
}

function onEdit() {
  fileInput.value.click();
}

async function onUnset() {
  busy.value = true;

  if (typeof props.unsetHandler === 'function') {
    const error = await props.unsetHandler();
    if (!error) isChanged.value = false;
  } else {
    isChanged.value = false;
  }

  internalSrc.value = props.fallbackSrc;
  emit('changed', 'fallback');

  busy.value = false;
}

function onChanged(event) {
  const file = event.target.files[0];
  if (!file) return;

  const fr = new FileReader();
  fr.onload = function () {
    const image = new Image();
    image.onload = function () {
      const canvas = document.createElement('canvas');

      if (props.maxSize) {
        if (image.naturalWidth > props.maxSize) {
          canvas.width = props.maxSize;
          canvas.height = (image.naturalHeight / image.naturalWidth) * props.maxSize;
        } else {
          canvas.width = image.naturalWidth;
          canvas.height = image.naturalHeight;
        }
      } else {
        canvas.width = props.size;
        canvas.height = props.size;
      }

      const imageDimensionRatio = image.width / image.height;
      const canvasDimensionRatio = canvas.width / canvas.height;
      let renderableHeight, renderableWidth, xStart, yStart;

      if (imageDimensionRatio > canvasDimensionRatio) {
        renderableHeight = canvas.height;
        renderableWidth = image.width * (renderableHeight / image.height);
        xStart = (canvas.width - renderableWidth) / 2;
        yStart = 0;
      } else if (imageDimensionRatio < canvasDimensionRatio) {
        renderableWidth = canvas.width;
        renderableHeight = image.height * (renderableWidth / image.width);
        xStart = 0;
        yStart = (canvas.height - renderableHeight) / 2;
      } else {
        renderableHeight = canvas.height;
        renderableWidth = canvas.width;
        xStart = 0;
        yStart = 0;
      }

      var ctx = canvas.getContext('2d');
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(image, xStart, yStart, renderableWidth, renderableHeight);

      internalSrc.value = canvas.toDataURL('image/png');
      isChanged.value = true;

      emit('changed', dataURLtoFile(internalSrc.value, 'image.png'));
    };

    image.src = fr.result;
  };
  fr.readAsDataURL(event.target.files[0]);
}

function onError() {
  internalSrc.value = props.fallbackSrc;
}

</script>

<template>
  <div>
    <div class="image-picker-container">
      <input @change="onChanged($event)" type="file" ref="fileInput" style="display: none" accept="image/*"/>

      <div ref="image" :disabled="disabled || null" class="image-picker" @click="!disabled && onEdit()">
        <img :src="internalSrc || src" @error="onError" class="image-picker-image" :style="{ height: displayHeight || null, width: displayWidth || null  }">
        <!-- Editable mode -->
        <template v-if="mode === 'editable'">
          <div v-if="isChanged" class="image-picker-actions" style="visibility: visible;">
            <Button @click.stop="onCancel" secondary tool icon="fa fa-undo" :disabled="busy"/>
            <Button @click.stop="onSave" success tool :loading="busy" :disabled="busy" icon="fa fa-floppy-disk"/>
          </div>
          <div v-else-if="!disabled" class="image-picker-actions">
            <Button @click.stop="onEdit" tool icon="fa fa-pencil-alt" :disabled="busy"/>
            <Button @click.stop="onUnset" tool icon="fa fa-trash" :loading="busy" :disabled="busy" v-if="unsetHandler && internalSrc !== fallbackSrc"/>
          </div>
        </template>
        <!-- Simple mode -->
        <template v-else-if="mode === 'simple'">
          <div class="image-picker-actions">
            <Button @click.stop="onEdit" tool icon="fa fa-pencil-alt" :disabled="busy"/>
            <Button v-if="isChanged" @click.stop="onCancel" secondary tool icon="fa fa-undo" :disabled="busy"/>
            <Button v-else-if="internalSrc !== fallbackSrc" @click.stop="onUnset" tool icon="fa fa-trash" :loading="busy" :disabled="busy"/>
          </div>
        </template>
      </div>
    </div>
  </div>
</template>

<style scoped>

.image-picker-container {
  position: relative;
  display: flex;
  gap: 6px;
  flex-direction: column;
}

.image-picker-actions {
  position: absolute;
  display: flex;
  gap: 6px;
  bottom: 6px;
  width: 100%;
  justify-content: center;
  visibility: hidden;
}

@media (hover: none) {
  .image-picker-actions {
    visibility: visible;
  }
}

.image-picker-actions:hover,
.image-picker:hover .image-picker-actions {
  visibility: visible;
}

.image-picker-image {
  margin-bottom: 16px;
}

.image-picker {
  display: inline-block;
  position: relative;
  cursor: pointer;
}

.image-picker[disabled] {
  cursor: not-allowed;
}

.image-picker > img {
  display: block;
/*  height: 320px;*/
  border-radius: 10px;
}

.image-picker-edit-indicator {
  position: absolute;
  bottom: 4px;
  right: 4px;
  border-radius: 20px;
  padding: 5px;
  color: var(--pankow-text-color);
  background-color: var(--pankow-input-background-color);
  transition: all 250ms;
}

.image-picker:hover .image-picker-edit-indicator {
  color: white;
  background: var(--pankow-color-primary);
  transform: scale(1.2);
}

</style>
