<template>
  <div
    ref="dragContainer"
    class="mark-body-container tw-flex-auto tw-p-2 tw-pt-0"
    @scroll="viewScroll"
  >
    <div v-if="!store.currentTask" class="tw-text-center none-tip">
      {{ store.message }}
    </div>
    <div
      v-else-if="!sliceImagesWithTrackList.length"
      class="tw-text-center none-tip"
    >
      考生答卷未上传
    </div>
    <div v-else :style="{ width: answerPaperScale }" class="tw-pt-2">
      <div
        v-for="(item, index) in sliceImagesWithTrackList"
        :key="index"
        class="single-image-container"
        :style="{
          width: item.width,
        }"
      >
        <img :src="item.url" draggable="false" />
        <MarkDrawTrack
          :trackList="item.trackList"
          :specialTagList="item.tagList"
          :sliceImageHeight="item.originalImageHeight"
          :sliceImageWidth="item.originalImageWidth"
          :dx="0"
          :dy="0"
        />
        <hr class="image-seperator" />
      </div>
    </div>
    <ZoomPaper v-if="store.isScanImage && sliceImagesWithTrackList.length" />
  </div>
</template>

<script setup lang="ts">
import { reactive, watch } from "vue";
import { store } from "@/store/store";
import MarkDrawTrack from "@/features/mark/MarkDrawTrack.vue";
import type { SpecialTag, Track, ColorMap } from "@/types";
import { useTimers } from "@/setups/useTimers";
import { loadImage } from "@/utils/utils";
import { dragImage } from "@/features/mark/use/draggable";
import ZoomPaper from "@/components/ZoomPaper.vue";

interface SliceImage {
  url: string;
  trackList: Array<Track>;
  tagList: Array<SpecialTag>;
  originalImageWidth: number;
  originalImageHeight: number;
  width: string; // 图片在整个图片列表里面的宽度比例
}

const { origImageUrls = "sliceUrls" } = defineProps<{
  origImageUrls?: "sheetUrls" | "sliceUrls";
}>();
const emit = defineEmits(["error", "getIsMultComments", "getScrollStatus"]);

const { dragContainer } = dragImage();
const viewScroll = () => {
  if (
    dragContainer.value.scrollTop + dragContainer.value.offsetHeight + 50 >=
    dragContainer.value.scrollHeight
  ) {
    emit("getScrollStatus");
  }
};
const { addTimeout } = useTimers();

let sliceImagesWithTrackList: SliceImage[] = reactive([]);
let maxImageWidth = 0;

function addTrackColorAttr(tList: Track[]): Track[] {
  let markerIds: (number | undefined)[] = tList
    .map((v) => v.markerId)
    .filter((x) => !!x);
  markerIds = Array.from(new Set(markerIds));
  markerIds.sort();
  let colorMap: ColorMap = {};
  for (let i = 0; i < markerIds.length; i++) {
    const mId: any = markerIds[i];
    if (i == 0) {
      colorMap[mId + ""] = "red";
    } else if (i == 1) {
      colorMap[mId + ""] = "blue";
    } else if (i > 1) {
      colorMap[mId + ""] = "green";
    }
  }
  if (Object.keys(colorMap).length > 1) {
    emit("getIsMultComments", true);
  }
  tList = tList.map((item: Track) => {
    item.color = colorMap[item.markerId + ""] || "red";
    item.isByMultMark = markerIds.length > 1;
    return item;
  });
  return tList;
}

function addTagColorAttr(tList: SpecialTag[]): SpecialTag[] {
  let markerIds: (number | undefined)[] = tList
    .map((v) => v.markerId)
    .filter((x) => !!x);
  markerIds = Array.from(new Set(markerIds));
  markerIds.sort();
  let colorMap: ColorMap = {};
  for (let i = 0; i < markerIds.length; i++) {
    const mId: any = markerIds[i];
    if (i == 0) {
      colorMap[mId + ""] = "red";
    } else if (i == 1) {
      colorMap[mId + ""] = "blue";
    } else if (i > 1) {
      colorMap[mId + ""] = "green";
    }
  }
  tList = tList.map((item: SpecialTag) => {
    item.color = colorMap[item.markerId + ""] || "red";
    item.isByMultMark = markerIds.length > 1;
    return item;
  });
  return tList;
}

async function processImage() {
  if (!store.currentTask) return;

  const images = [];
  const urls = store.currentTask[origImageUrls] || [];
  for (const url of urls) {
    const image = await loadImage(url);
    images.push(image);
  }

  maxImageWidth = Math.max(...images.map((i) => i.naturalWidth));

  for (const url of urls) {
    const indexInSliceUrls = urls.indexOf(url) + 1;
    const image = images[indexInSliceUrls - 1];

    const trackLists = (store.currentTask.questionList || [])
      // .map((q) => q.trackList)
      .map((q) => {
        let tList = q.trackList;

        return addTrackColorAttr(tList);
      })
      .flat();
    const thisImageTrackList = trackLists.filter(
      (t) => t.offsetIndex === indexInSliceUrls
    );
    const thisImageTagList = addTagColorAttr(
      (store.currentTask.specialTagList || []).filter(
        (t) => t.offsetIndex === indexInSliceUrls
      )
    );

    sliceImagesWithTrackList.push({
      url,
      trackList: thisImageTrackList,
      tagList: thisImageTagList,
      originalImageWidth: image.naturalWidth,
      originalImageHeight: image.naturalHeight,
      width: (image.naturalWidth / maxImageWidth) * 100 + "%",
    });
  }
}

// should not render twice at the same time
let renderLock = false;
const renderPaperAndMark = async () => {
  if (renderLock) {
    console.log("上个任务还未渲染完毕，稍等一秒再尝试渲染");
    await new Promise((res) => setTimeout(res, 1000));
    await renderPaperAndMark();
    return;
  }
  renderLock = true;
  sliceImagesWithTrackList.splice(0);

  if (!store.currentTask) {
    renderLock = false;
    return;
  }

  try {
    store.globalMask = true;
    await processImage();
  } catch (error) {
    sliceImagesWithTrackList.splice(0);
    console.log("render error ", error);
    // 图片加载出错，自动加载下一个任务
    emit("error");
  } finally {
    await new Promise((res) => setTimeout(res, 500));
    store.globalMask = false;
    renderLock = false;
  }
};

watch(() => store.currentTask, renderPaperAndMark);

watch(
  (): (number | undefined)[] => [
    store.minimapScrollToX,
    store.minimapScrollToY,
  ],
  () => {
    const container = document.querySelector<HTMLDivElement>(
      ".mark-body-container"
    );
    addTimeout(() => {
      if (
        container &&
        typeof store.minimapScrollToX === "number" &&
        typeof store.minimapScrollToY === "number"
      ) {
        const { scrollWidth, scrollHeight } = container;
        container.scrollTo({
          top: scrollHeight * store.minimapScrollToY,
          left: scrollWidth * store.minimapScrollToX,
          behavior: "smooth",
        });
      }
    }, 10);
  }
);

const answerPaperScale = $computed(() => {
  // 放大、缩小不影响页面之前的滚动条定位
  let percentWidth = 0;
  let percentTop = 0;
  const container = document.querySelector(".mark-body-container");
  if (container) {
    const { scrollLeft, scrollTop, scrollWidth, scrollHeight } = container;
    percentWidth = scrollLeft / scrollWidth;
    percentTop = scrollTop / scrollHeight;
  }

  addTimeout(() => {
    if (container) {
      const { scrollWidth, scrollHeight } = container;
      container.scrollTo({
        left: scrollWidth * percentWidth,
        top: scrollHeight * percentTop,
      });
    }
  }, 10);
  const scale = store.setting.uiSetting["answer.paper.scale"];
  return scale * 100 + "%";
});
</script>

<style scoped>
.mark-body-container .none-tip {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 28px;
}
.mark-body-container {
  height: calc(100vh - 56px);
  overflow: auto;
  background-color: var(--app-container-bg-color);
  background-image: linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
    linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
    linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
  transform: inherit;

  cursor: grab;
  user-select: none;
}
.mark-body-container img {
  width: 100%;
}
.single-image-container {
  position: relative;
}
.image-seperator {
  border: 2px solid rgba(120, 120, 120, 0.1);
}
</style>
