<template>
  <div ref="rowWrapper" class="grid-item-wrapper"
    :draggable="!rename"
    @mouseup="onSelect($event)"
    @drop="onDrop($event)"
    @dragover="onDragOver($event)"
    @dragexit="onDragExit($event)"
    >

    <div class="grid-item" :class="{ 'focused': item.focused, 'selected': item.selected, 'drop-target-active': dropTargetActive }">
      <div v-if="visible && showStar" class="grid-item-star" @dblclick.stop.prevent @click.stop.prevent="onToggleStar">
        <Icon :icon="`${item.star ? 'fa-solid' : 'fa-regular'} fa-star`" class="star-icon" :class="{ 'star-visible': item.star }" />
      </div>
      <div v-if="visible" class="grid-item-actions" @mouseup.stop="onActionMenu($event)">
        <i class="fa-solid fa-ellipsis-vertical"/>
      </div>

      <div class="grid-item-icon">
        <template v-if="visible">
          <img :src="item.previewUrl || item.icon" ref="iconImage" @error="iconError($event)"/>
          <i v-show="shareIndicatorProperty !== '' && !!item[shareIndicatorProperty]" class="fa-solid fa-share-nodes is-shared"></i>
        </template>
      </div>

      <!-- this is just a placeholder label to allow the native browser search to work -->
      <div v-if="!visible">
        {{ item.name }}
      </div>
      <div v-else-if="visible && !rename" class="grid-item-label" :title="item.name + (item.target ? ` → ${item.target}` : '')">
        <a v-if="item.href" class="open-action" @dblclick.stop @click="onOpen($event)" :href="item.href">{{ item.name }}</a>
        <span v-else>{{ item.name }}</span>
      </div>
      <div v-else-if="visible && rename" ref="renameCell" class="grid-item-label grid-item-rename">
        <TextInput
          v-model="newName"
          :disabled="renameBusy"
          @blur="onRenameEnd"
          @dblclick.stop
          @keydown.enter.stop="onRenameSubmit"
          @keydown.esc.stop="onRenameEnd"
          @keydown.stop
          />
      </div>
    </div>
  </div>
</template>

<script setup>

import { ref, onMounted } from 'vue';

import Icon from './Icon.vue';
import TextInput from './TextInput.vue';

const props = defineProps({
  item: Object,
  showStar: {
    type: Boolean,
    default: false,
  },
  shareIndicatorProperty: {
    type: String,
    default: '',
  },
  fallbackIcon: String,
  renameHandler: {
    type: Function,
    default() {
      console.warn('Missing renameHandler for DirectoryViewGridItem');
    },
  },
  starHandler: {
    type: Function,
    default() {
      console.warn('Missing starHandler for DirectoryViewGridItem');
    },
  },
  selectHandler: {
    type: Function,
    default() {
      console.warn('Missing selectHandler for DirectoryViewGridItem');
    },
  },
  dropHandler: {
    type: Function,
    default() {
      console.warn('Missing dropHandler for DirectoryViewGridItem');
    },
  },
  canDropHandler: {
    type: Function,
    default() {
      console.warn('Missing canDropHandler for DirectoryViewGridItem');
    },
  },
});

const emit = defineEmits(['activated', 'action-menu']);

const rowWrapper = ref(null);
const renameCell = ref(null);

const visible = ref(false);
const rename = ref(false);
const renameBusy = ref(false);
const newName = ref('');
const dropTargetActive = ref(false);
const previewRetries = ref(0);

function highlight() {
  rowWrapper.value?.classList.add('pankow-directory-view-highlight-animation');
  setTimeout(() => {
    rowWrapper.value?.classList.remove('pankow-directory-view-highlight-animation');
  }, 4000);
}

function onToggleStar() {
  props.starHandler(props.item);
}

function onActionMenu(event) {
  onSelect(event, true);
  emit('action-menu', props.item, event);
}

function onRenameBegin() {
  rename.value = true;
  newName.value = props.item.name;

  setTimeout(() => {
    const elem = renameCell.value?.querySelector('input.pankow-text-input');
    if (!elem) return;

    elem.focus();

    if (typeof elem.selectionStart !== 'undefined') {
      elem.selectionStart = 0;
      elem.selectionEnd = props.item.name.lastIndexOf('.');
    }
  }, 0);
}

function onRenameEnd() {
  rename.value = false;
  renameBusy.value = false;
  newName.value = '';
}

async function onRenameSubmit() {
  if (!newName.value) return;

  renameBusy.value = true;
  await props.renameHandler(props.item, newName.value);
  onRenameEnd();
}

function onOpen(event) {
  if (event.ctrlKey || event.metaKey) return;

  event.preventDefault();
  event.stopPropagation();

  emit('activated', props.item);
}

function onSelect(event, actionMenu = false) {
  if ((event.button === 2 || actionMenu) && event.ctrlKey) {
    props.selectHandler(props.item, event, true);
  } else {
    props.selectHandler(props.item, event);
  }
}

function onDrop(event) {
  if (!props.canDropHandler(props.item)) return;

  event.stopPropagation();

  props.dropHandler(props.item, event);
  dropTargetActive.value = false;
}

function onDragOver(event) {
  if (!props.canDropHandler(props.item)) return;

  event.preventDefault();
  event.stopPropagation();

  event.dataTransfer.dropEffect = 'move';
  dropTargetActive.value = true;
}

function onDragExit() {
  if (!props.item.isDirectory) return;

  dropTargetActive.value = false;
}

function iconError(event) {
  event.target.src = props.fallbackIcon;

  setTimeout(() => {
    if (previewRetries.value > 5) return;
    previewRetries.value += 1;

    event.target.src = props.item.previewUrl || props.item.icon;
  }, 1000);
}

onMounted(() => {
  const el = rowWrapper.value;
  if (!el) return;

  const observer = new IntersectionObserver((result) => {
    if (result[0].isIntersecting) {
      visible.value = true;
      observer.unobserve(el);
    }
  });

  observer.observe(el);
});

defineExpose({
  highlight,
  onRenameBegin,
  get $el() {
    return rowWrapper.value;
  },
});

</script>

<style scoped>

.grid-item-wrapper {
  --background-color-hover: #ededed;
  --background-color-selected: #dbedfb;
  --border-color-focus: #b3cfe5;
}

@media (prefers-color-scheme: dark) {
  .grid-item-wrapper {
    --background-color-hover: rgba(255, 255, 255, 0.1);
    --background-color-selected: rgba(255, 255, 255, 0.2);
    --border-color-focus: rgba(255, 255, 255, 0.3);
  }
}

.grid-item-wrapper {
  padding: 2px;
}

.grid-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 8px 8px;
  border-radius: 6px;
  border: 2px solid transparent;
  position: relative;
  transition: all 100ms;
  cursor: default;
}

.grid-item-wrapper:hover > .grid-item {
  background-color: var(--background-color-hover);
}

.grid-item.drop-target-active {
  border: 2px solid var(--pankow-color-primary);
}

.grid-item.selected {
  background-color: var(--background-color-selected) !important;
}

.grid-item.focused {
  border: solid 2px var(--border-color-focus);
}

.grid-item-actions {
  position: absolute;
  top: 4px;
  right: 4px;
  padding: 2px 6px;
  cursor: pointer;
  border-radius: 3px;
  opacity: 0;
  transition: opacity 100ms;
}

.grid-item-actions:hover {
  background-color: var(--background-color-hover);
}

.grid-item-wrapper:hover .grid-item-actions,
.grid-item.focused .grid-item-actions,
.grid-item.selected .grid-item-actions {
  opacity: 1;
}

.grid-item-icon {
  width: 80px;
  height: 80px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  flex-shrink: 0;
}

.grid-item-icon > img {
  max-width: 64px;
  max-height: 64px;
  object-fit: contain;
}

.is-shared {
  position: absolute;
  right: 2px;
  bottom: 2px;
  font-size: 1rem;
}

.grid-item-label {
  width: 100%;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 4px;
  font-size: 0.85rem;
  padding: 0 2px;
}

.grid-item-rename {
  overflow: hidden;
}

.grid-item-rename :deep(input) {
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  font-size: 0.85rem;
  padding: 1px 4px;
}

.grid-item-star {
  position: absolute;
  top: 4px;
  left: 4px;
  padding: 2px 6px;
  cursor: pointer;
  border-radius: 3px;
  opacity: 0;
  transition: opacity 100ms;
}

.grid-item-star:hover {
  background-color: var(--background-color-hover);
}

.grid-item-wrapper:hover .grid-item-star,
.grid-item.focused .grid-item-star,
.grid-item.selected .grid-item-star {
  opacity: 1;
}

.star-icon {
  color: #ffcb00;
  cursor: pointer;
  font-size: 0.8rem;
}

.grid-item-star:has(.star-visible) {
  opacity: 1 !important;
}

.open-action {
  cursor: pointer;
}

.open-action:hover {
  text-decoration: underline;
}

</style>
