<template>
  <component :is="rootComponent" ref="root" v-bind="{ ...$props, ...$attrs }" v-if="showMultiview">
    <div class="bubble">
      <MediaIntervention :multiple="items.length > 1" :mediaInterventionFlags="mediaInterventionFlags" />
      <div class="original-message bubble-inset" v-if="inReplyToText">
        <div class="original-message-sender">{{ inReplyToSender }}</div>
        <div class="original-message-text" v-html="linkify($$sanitize(inReplyToText))" />
      </div>

      <div class="message">
        <SwipeableThumbnailsView
          :items="items"
          v-if="event && !event.isRedacted() && room.displayType == ROOM_TYPE_CHANNEL"
          v-bind="$attrs"
        />
        <v-container v-else-if="event && !event.isRedacted()" fluid class="imageCollection">
          <v-row class="pa-0 ma-0" wrap>
            <v-col class="pa-0 ma-0" v-for="{ size, item } in layoutedItems" :key="item.event.getId()" :cols="size">
              <ThumbnailView :item="item" :previewOnly="true" v-on:itemclick="onItemClick($event)" />
            </v-col>
          </v-row>
        </v-container>
        <div class="bubble-inset" v-if="showMessageText || event?.isRedacted() || event?.replacingEventId()">
          <i v-if="event && event.isRedacted()" class="deleted-text">
            <v-icon :color="isIncoming && senderIsAdminOrModerator(event) ? 'white' : ''" size="small">block</v-icon>
            {{
              redactedBySomeoneElse(event)
                ? $t("message.incoming_message_deleted_text")
                : $t("message.outgoing_message_deleted_text")
            }}
          </i>
          <span v-html="linkify($$sanitize(messageText))" v-else-if="messageText" />
          <span class="edit-marker" v-if="event && event.replacingEventId() && !event.isRedacted()">
            {{ t("message.edited") }}
          </span>
        </div>
      </div>
    </div>
    <GalleryItemsView
      :originalEvent="originalEvent"
      :items="items"
      :initialItem="showItem"
      v-if="!!showItem"
      v-on:close="showItem = undefined"
    />
  </component>
  <component
    v-else-if="items.length == 1"
    :is="$props.componentFn(items[0].event, false)"
    v-bind="{ ...$props, ...$attrs }"
    :originalEvent="items[0].event"
  />
</template>

<script setup lang="ts">
import MessageIncoming from "./MessageIncoming.vue";
import MessageOutgoing from "./MessageOutgoing.vue";
import { MessageProps, useMessage } from "./useMessage";
import util, { ROOM_TYPE_CHANNEL, ROOM_TYPE_FILE_MODE } from "@/plugins/utils";
import GalleryItemsView from "../../file_mode/GalleryItemsView.vue";
import ThumbnailView from "../../file_mode/ThumbnailView.vue";
import SwipeableThumbnailsView from "../channel/SwipeableThumbnailsView.vue";
import { computed, inject, onBeforeUnmount, ref, Ref, useTemplateRef, watch } from "vue";
import { EventAttachment } from "../../../models/eventAttachment";
import { useI18n } from "vue-i18n";
import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk";
import { useLazyLoad } from "./useLazyLoad";
import { MediaInterventionFlags } from "../../../models/proof";
import MediaIntervention from "../../content-credentials/MediaIntervention.vue";

const { t } = useI18n();
const $matrix: any = inject("globalMatrix");
const $$sanitize: any = inject("globalSanitize");

type RootType = InstanceType<typeof MessageOutgoing | typeof MessageIncoming>;
const rootRef = useTemplateRef<RootType>("root");

const emits = defineEmits<{
  (event: "layout-change", value: { element: Element | undefined; action: () => void }): void;
}>();

const items: Ref<EventAttachment[]> = ref([]);
const showItem: Ref<EventAttachment | undefined> = ref(undefined);

const props = defineProps<MessageProps>();

const { room } = props;

const processThread = () => {
  if (!event.value?.isRedacted()) {
    const el = rootRef.value?.$el;
    emits("layout-change", { element: el, action: _processThread });
  }
};

const { isVisible } = useLazyLoad({ root: rootRef });

const {
  event,
  thread,
  isIncoming,
  senderIsAdminOrModerator,
  inReplyToSender,
  inReplyToText,
  messageText,
  redactedBySomeoneElse,
  linkify,
} = useMessage($matrix, t, props, undefined, processThread);

const rootComponent = computed(() => {
  return isIncoming.value ? MessageIncoming : MessageOutgoing;
});

const onRelationsCreated = () => {
  if (event.value) {
    thread.value = props.timelineSet.relations.getChildEventsForEvent(
      event.value.getId() ?? "",
      util.threadMessageType(),
      "m.room.message"
    );
    event.value.off(MatrixEventEvent.RelationsCreated, onRelationsCreated);
  }
};

watch(
  event,
  () => {
    if (event.value) {
      if (["m.image", "m.video"].includes(event.value.getContent().msgtype ?? "")) {
        // Single image mode
        items.value = [event.value].map((e: MatrixEvent) => {
          let ea = $matrix.attachmentManager.getEventAttachment(e);
          if (isVisible.value) {
            ea.loadThumbnail();
          }
          return ea;
        });
      } else if (thread.value === undefined) {
        thread.value = props.timelineSet.relations.getChildEventsForEvent(
          event.value.getId() ?? "",
          util.threadMessageType(),
          "m.room.message"
        );
      }
      if (!thread.value) {
        event.value.on(MatrixEventEvent.RelationsCreated, onRelationsCreated);
      }
    }
  },
  { immediate: true }
);

onBeforeUnmount(() => {
  event.value?.off(MatrixEventEvent.RelationsCreated, onRelationsCreated);
});

const showMessageText = computed((): boolean => {
  if (["m.image", "m.video"].includes(event.value?.getContent().msgtype ?? "")) {
    return false;
  }
  return true;
});

const isSinglePDF = computed(() => items.value.length === 1 && util.isFileTypePDF(items.value[0].event))

const showMultiview = computed((): boolean => {
  if (["m.image", "m.video"].includes(event.value?.getContent().msgtype ?? "")) {
    return true;
  }
  return (
    (isIncoming.value && props.room.displayType == ROOM_TYPE_FILE_MODE) ||
    items.value?.length > 1 ||
    (event.value && event.value.isRedacted()) ||
    (props.room.displayType == ROOM_TYPE_CHANNEL && isSinglePDF.value) ||
    messageText.value?.length > 0 || isSinglePDF.value
  );
});

const mediaInterventionFlags = computed(() => {
  return items.value.reduce((res: MediaInterventionFlags[], item) => {
    if (item.mediaInterventionFlags) {
      res.push(item.mediaInterventionFlags);
    }
    return res;
  }, []);
});

watch(isVisible, (visible) => {
  if (showMultiview.value && visible) {
    items.value.forEach((a) => a.loadThumbnail());
  }
});

const onItemClick = (event: any) => {
  showItem.value = event.item;
};

const _processThread = () => {
  const eventItems = props.timelineSet.relations
    .getAllChildEventsForEvent(event.value?.getId() ?? "")
    .filter((e: MatrixEvent) => !e.isRedacted() && util.downloadableTypes().includes(e.getContent().msgtype));

  items.value = eventItems.map((e: MatrixEvent) => {
    let ea = $matrix.attachmentManager.getEventAttachment(e);
    if (showMultiview.value && isVisible.value) {
      ea.loadThumbnail();
    }
    return ea;
  });
};

const layoutedItems = computed(() => {
  if (!items.value || items.value.length == 0) {
    return [];
  }
  let array = items.value.slice(0);
  let rows = [];
  while (array.length > 0) {
    if (array.length >= 7) {
      rows.push({ size: 6, item: array[0] });
      rows.push({ size: 6, item: array[1] });
      rows.push({ size: 12, item: array[2] });
      rows.push({ size: 3, item: array[3] });
      rows.push({ size: 3, item: array[4] });
      rows.push({ size: 3, item: array[5] });
      rows.push({ size: 3, item: array[6] });
      array = array.slice(7);
    } else if (array.length >= 3) {
      rows.push({ size: 12, item: array[0] });
      rows.push({ size: 6, item: array[1] });
      rows.push({ size: 6, item: array[2] });
      array = array.slice(3);
    } else if (array.length >= 2) {
      rows.push({ size: 6, item: array[0] });
      rows.push({ size: 6, item: array[1] });
      array = array.slice(2);
    } else {
      rows.push({ size: 12, item: array[0] });
      array = array.slice(1);
    }
  }
  return rows;
});
</script>
<style lang="scss">
@use "@/assets/css/chat.scss" as *;
</style>

<style lang="scss" scoped>
.bubble {
  width: 100%;
  margin: 0 0 !important;
  padding: 0 !important;
  overflow: hidden;
}

.imageCollection {
  width: unset;
  max-width: unset;
  margin: 0 0px !important;
  padding: 0 !important;
  overflow: hidden;

  .v-row {
    margin: -2px -2px 0 -2px !important;
  }

  .v-col {
    padding: 2px !important;
    background-color: transparent !important;
  }

  .file-item {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.6rem;
    flex-direction: column;
    padding: 20px;
  }
}
</style>
