<template>
  <div class="pankow-file-uploader" v-show="uploadInProgress">
    <div class="pankow-file-uploader-label-and-speed-container"><div class="pankow-file-uploader-label">{{ tr('filemanager.uploader.uploading') }} {{ activeFile ? activeFile.name : '' }}</div><div class="pankow-file-uploader-speed">{{ prettyUploadSpeed }}</div></div>
    <div class="pankow-file-uploader-progress-container">
      <ProgressBar :value="parseInt((uploadedSize * 100) / size) || 0" style="flex-grow: 1"></ProgressBar>
      <Button danger small tool @click="onCancelUpload" icon="fa-solid fa-xmark"></Button>
    </div>

    <input type="file" ref="uploadFileInput" style="display: none" multiple/>
    <input type="file" ref="uploadFolderInput" style="display: none" multiple webkitdirectory directory/>

    <InputDialog ref="inputDialog" />
  </div>
</template>

<script setup>

import { ref, computed, onMounted, useTemplateRef } from 'vue';

import ProgressBar from './ProgressBar.vue';
import Button from './Button.vue';
import InputDialog from './InputDialog.vue';

import { translation } from '../utils.js';

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

const props = defineProps({
  uploadHandler: {
    type: Function,
    default() { console.warn('Missing uploadHandler for FileUploader'); }
  },
  cancelHandler: {
    type: Function,
    default() { console.warn('Missing cancelHandler for FileUploader'); }
  },
  jobPreFlightCheckHandler: {
    type: Function,
    default(job) { console.warn('Missing jobPreFlightCheckHandler for FileUploader'); return true; }
  },
  tr: {
    type: Function,
    default(id) { console.warn('Missing tr for FileUploader, using fallback.'); return translation(id); }
  },
});

const uploadFileInput = useTemplateRef('uploadFileInput');
const uploadFolderInput = useTemplateRef('uploadFolderInput');
const inputDialog = useTemplateRef('inputDialog');

const activeTargetFolder = ref('/');
const uploadInProgress = ref(false);
const activeJob = ref(null);
const activeFile = ref({});
const pendingJobs = ref([]);
const size = ref(0);
const uploadedSize = ref(0);
const uploadSpeed = ref(0);

const prettyUploadSpeed = computed(() => {
  if (uploadSpeed.value < 1024) return uploadSpeed.value + ' Kb/s';
  else return (uploadSpeed.value / 1024).toFixed(2) + ' Mb/s';
});

function beforeUnloadListener(event) {
  event.preventDefault();
  return window.confirm(props.tr('filemanager.uploader.exitWarning'));
}

function onUploadFile(targetFolder) {
  activeTargetFolder.value = targetFolder;
  uploadFileInput.value?.click();
}

function onUploadFolder(targetFolder) {
  activeTargetFolder.value = targetFolder;
  uploadFolderInput.value?.click();
}

async function onCancelUpload() {
  const confirmed = await inputDialog.value.confirm({
    message: `Really cancel upload?`,
    confirmStyle: 'danger',
    confirmLabel: 'Yes',
    rejectLabel: 'No'
  });

  if (!confirmed) return;

  props.cancelHandler();
  uploadFinished();
}

function uploadFinished() {
  size.value = 0;
  uploadedSize.value = 0;
  activeFile.value = {};
  activeJob.value = null;
  uploadInProgress.value = false;

  window.removeEventListener('beforeunload', beforeUnloadListener, { capture: true });

  emit('finished');
}

async function uploadNextJob() {
  if (pendingJobs.value.length === 0) return uploadFinished();

  activeJob.value = pendingJobs.value.pop();

  if (!(await props.jobPreFlightCheckHandler(activeJob.value))) return uploadNextJob();

  uploadNextFile();
}

async function uploadNextFile() {
  console.log('next file')
  if (!activeJob.value || activeJob.value.pendingFiles.length === 0) return uploadNextJob();

  activeFile.value = activeJob.value.pendingFiles.pop();

  try {
    let uploadedSoFar = 0;
    let lastTimestamp = Date.now();

    await props.uploadHandler(activeJob.value.targetFolder, activeFile.value, (event) => {
      if (event.direction === 'upload') {
        const timeDiff = Date.now() - lastTimestamp;
        const loadedDiff = event.loaded - uploadedSoFar;

        uploadedSize.value += loadedDiff;
        uploadSpeed.value = parseInt(loadedDiff / (timeDiff / 1024) / 1024);

        uploadedSoFar = event.loaded;
        lastTimestamp = Date.now();
      }
    });
  } catch (e) {
    console.error(`Failed to upload ${activeFile.value.name}`, e);
  }

  uploadNextFile();
}

function startUpload() {
  if (uploadInProgress.value) return;
  uploadInProgress.value = true;

  window.addEventListener('beforeunload', beforeUnloadListener, { capture: true });

  uploadNextJob();
}

function addFiles(files, targetFolder) {
  if (!files || !files.length) return;

  let folder = files[0].webkitRelativePath ? files[0].webkitRelativePath : null;
  if (folder && folder.indexOf('/') !== -1) folder = folder.split('/')[0];

  const job = {
    folder,
    targetFolder,
    pendingFiles: []
  };

  for (let i = 0; i < files.length; ++i) {
    size.value += files[i].size;
    job.pendingFiles.push(files[i]);
  }

  pendingJobs.value.push(job);

  startUpload();
}

onMounted(() => {
  uploadFileInput.value?.addEventListener('change', (e) => {
    addFiles(e.target.files || [], activeTargetFolder.value);
  });

  uploadFolderInput.value?.addEventListener('change', (e) => {
    addFiles(e.target.files || [], activeTargetFolder.value);
  });
});

defineExpose({
  addFiles,
  onUploadFile,
  onUploadFolder
});

</script>

<style scoped>

.label {
  padding-bottom: 5px;
}

</style>

<style>

.pankow-file-uploader {
  width: 100%;
  padding: 10px;
  background-color: var(--pankow-color-background);
}

.pankow-file-uploader-label-and-speed-container {
  display: flex;
  gap: 10px;
  margin-bottom: 5px;
}

.pankow-file-uploader-label {
  flex-grow: 1;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

.pankow-file-uploader-speed {
  white-space: nowrap;
}

.pankow-file-uploader-progress-container {
  display: flex;
  gap: 10px;
}

</style>
