<template>
  <div
    class="mark-body-container tw-flex-auto tw-p-2 tw-relative"
    ref="dragContainer"
  >
    <div
      v-if="!store.currentTask"
      class="
        tw-text-center
        empty-task
        tw-flex tw-flex-col tw-place-items-center tw-justify-center
      "
    >
      <img src="./images/empty-task.png" />
      {{ store.message }}
    </div>
    <div
      v-else-if="store.isScanImage"
      :style="{ width: answerPaperScale }"
      :class="[`rotate-board-${rotateBoard}`]"
    >
      <div
        v-for="(item, index) in sliceImagesWithTrackList"
        :key="index"
        class="single-image-container"
      >
        <img
          :src="item.url"
          @click="(event) => innerMakeTrack(event, item)"
          draggable="false"
          @contextmenu="showBigImage"
        />
        <MarkDrawTrack
          :track-list="item.trackList"
          :special-tag-list="item.tagList"
          :slice-image-width="item.sliceImageWidth"
          :slice-image-height="item.sliceImageHeight"
          :dx="item.dx"
          :dy="item.dy"
        />
        <hr class="image-seperator" />
      </div>
    </div>
    <div v-else-if="store.isMultiMedia">
      <MultiMediaMarkBody />
    </div>
    <div v-else>impossible</div>
    <div v-if="markStatus" class="status-container">
      {{ markStatus }}
      <div class="double-triangle"></div>
    </div>
    <ZoomPaper v-if="store.isScanImage && store.currentTask" :store="store" />
  </div>
  <slot name="slot-cursor" />
</template>

<script setup lang="ts">
import { onMounted, onUnmounted, reactive, watch, watchEffect } from "vue-demi";
import { store } from "@/store/store";
import MarkDrawTrack from "./MarkDrawTrack.vue";
import type {
  MarkResult,
  MarkStore,
  SliceImage,
  SpecialTag,
  Track,
} from "@/types";
import { useTimers } from "@/setups/useTimers";
import {
  getDataUrlForSliceConfig,
  getDataUrlForSplitConfig,
  loadImage,
} from "@/utils/utils";
import { dragImage } from "./use/draggable";
import MultiMediaMarkBody from "./MultiMediaMarkBody.vue";
import "viewerjs/dist/viewer.css";
import Viewer from "viewerjs";
import ZoomPaper from "@/components/ZoomPaper.vue";

const props = defineProps<{
  useMarkResult?: boolean;
  makeTrack: Function;
  store: MarkStore; // 以前不是一个类型的store，现在变成一样的了，所以这个可以删掉了，并且可以优化这个组件的使用
}>();

const emit = defineEmits(["error"]);

const { useMarkResult = false, makeTrack } = props;

// start: 图片拖动。在轨迹模式下，仅当没有选择分数时可用。
const { dragContainer } = dragImage();
// end: 图片拖动

const { addTimeout } = useTimers();
let rotateBoard = $ref(0);

// start: 缩略图定位
watch(
  () => [store.minimapScrollToX, store.minimapScrollToY],
  () => {
    const container = document.querySelector(
      ".mark-body-container"
    ) as HTMLDivElement;
    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);
  }
);
// end: 缩略图定位

// start: 快捷键定位
const scrollContainerByKey = (e: KeyboardEvent) => {
  const container = document.querySelector(
    ".mark-body-container"
  ) as HTMLDivElement;
  if (e.key === "w") {
    container.scrollBy({ top: -100, behavior: "smooth" });
  } else if (e.key === "s") {
    container.scrollBy({ top: 100, behavior: "smooth" });
  } else if (e.key === "a") {
    container.scrollBy({ left: -100, behavior: "smooth" });
  } else if (e.key === "d") {
    container.scrollBy({ left: 100, behavior: "smooth" });
  }
};
onMounted(() => {
  document.addEventListener("keypress", scrollContainerByKey);
});
onUnmounted(() => {
  document.removeEventListener("keypress", scrollContainerByKey);
});
// end: 快捷键定位

// start: 计算裁切图和裁切图上的分数轨迹和特殊标记轨迹
let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
let maxSliceWidth = 0; // 最大的裁切块宽度，图片容器以此为准
let theFinalHeight = 0; // 最终宽度，用来定位轨迹在第几张图片，不包括image-seperator高度

async function processSliceConfig() {
  let markResult = store.currentTask?.markResult as MarkResult;
  if (useMarkResult) {
    // check if have MarkResult for currentTask
    if (!markResult) return;
  }

  if (!store.currentTask) return;

  const images = [];
  // 必须要先加载一遍，把“选择整图”的宽高重置后，再算总高度
  for (const sliceConfig of store.currentTask.sliceConfig) {
    const url = store.currentTask.sliceUrls[sliceConfig.i - 1];
    const image = await loadImage(url);
    images[sliceConfig.i] = image;
    if (sliceConfig.w === 0 && sliceConfig.h === 0) {
      // 选择整图时，w/h 为0
      sliceConfig.w = image.naturalWidth;
      sliceConfig.h = image.naturalHeight;
    }
  }

  theFinalHeight = store.currentTask.sliceConfig
    .map((v) => v.h)
    .reduce((acc, v) => (acc += v));
  maxSliceWidth = Math.max(...store.currentTask.sliceConfig.map((v) => v.w));

  // 用来保存sliceImage在整个图片容器中(不包括image-seperator)的高度范围
  let accumTopHeight = 0;
  let accumBottomHeight = 0;
  const tempSliceImagesWithTrackList = [] as Array<SliceImage>;
  for (const sliceConfig of store.currentTask.sliceConfig) {
    accumBottomHeight += sliceConfig.h;
    const url = store.currentTask.sliceUrls[sliceConfig.i - 1];
    const indexInSliceUrls = sliceConfig.i;
    const image = images[sliceConfig.i];

    const dataUrl = await getDataUrlForSliceConfig(
      image,
      sliceConfig,
      maxSliceWidth,
      url
    );

    let trackLists = [] as Array<Track>;
    if (useMarkResult) {
      trackLists = markResult.trackList;
    } else {
      trackLists = store.currentTask.questionList
        .map((q) => q.trackList)
        .reduce((acc, t) => {
          acc = acc.concat(t);
          return acc;
        }, [] as Array<Track>);
    }
    const thisImageTrackList = trackLists.filter(
      (t) => t.offsetIndex === indexInSliceUrls
    );

    let tagLists = [] as Array<SpecialTag>;
    if (useMarkResult) {
      tagLists = markResult.specialTagList ?? [];
    } else {
      tagLists = store.currentTask.specialTagList ?? [];
    }
    const thisImageTagList = tagLists.filter(
      (t) => t.offsetIndex === indexInSliceUrls
    );

    const sliceImageRendered = await loadImage(dataUrl!);
    tempSliceImagesWithTrackList.push({
      url: dataUrl!,
      indexInSliceUrls: sliceConfig.i,
      // 通过positionY来定位是第几张slice的还原，并过滤出相应的track
      trackList: thisImageTrackList.filter(
        (t) =>
          t.positionY >= accumTopHeight / theFinalHeight &&
          t.positionY < accumBottomHeight / theFinalHeight
      ),
      tagList: thisImageTagList.filter(
        (t) =>
          t.positionY >= accumTopHeight / theFinalHeight &&
          t.positionY < accumBottomHeight / theFinalHeight
      ),
      // originalImageWidth: image.naturalWidth,
      // originalImageHeight: image.naturalHeight,
      sliceImageWidth: sliceImageRendered.naturalWidth,
      sliceImageHeight: sliceImageRendered.naturalHeight,
      dx: sliceConfig.x,
      dy: sliceConfig.y,
      accumTopHeight,
      effectiveWidth: sliceConfig.w,
    });
    accumTopHeight = accumBottomHeight;
  }
  // console.log("render: ", store.currentTask.secretNumber);
  rotateBoard = 1;
  setTimeout(() => {
    sliceImagesWithTrackList.splice(0);
    sliceImagesWithTrackList.push(...tempSliceImagesWithTrackList);
    setTimeout(() => {
      rotateBoard = 0;
    }, 300);
  }, 300);
}

async function processSplitConfig() {
  let markResult = store.currentTask?.markResult as MarkResult;
  if (useMarkResult) {
    // check if have MarkResult for currentTask
    if (!markResult) return;
  }

  if (!store.currentTask) return;

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

  // 裁切块，可能是一块，两块，三块... [start, width ...] => [0, 0.3] | [0, 0.55, 0.45, 0.55] | [0, 0.35, 0.33, 0.35, 0.66, 0.35]
  const splitConfigPairs = store.setting.splitConfig
    .map((v, index, ary) => (index % 2 === 0 ? [v, ary[index + 1]] : false))
    .filter((v) => v) as unknown as Array<[number, number]>;

  // 最大的 splitConfig 的宽度
  const maxSplitConfig = Math.max(
    ...store.setting.splitConfig.filter((v, i) => i % 2)
  );
  maxSliceWidth =
    Math.max(...images.map((v) => v.naturalWidth)) * maxSplitConfig;

  theFinalHeight =
    splitConfigPairs.length *
    images.reduce((acc, v) => (acc += v.naturalHeight), 0);

  // 高度比宽度大的图片不裁切
  const imagesOfBiggerHeight = images.filter(
    (v) => v.naturalHeight > v.naturalWidth
  );
  if (imagesOfBiggerHeight.length > 0) {
    maxSliceWidth = Math.max(
      maxSliceWidth,
      ...imagesOfBiggerHeight.map((v) => v.naturalWidth)
    );
    // 不裁切的图剪切多加的高度
    theFinalHeight -=
      imagesOfBiggerHeight.map((v) => v.naturalHeight).reduce((p, c) => p + c) *
      (splitConfigPairs.length - 1);
  }

  let accumTopHeight = 0;
  let accumBottomHeight = 0;
  const tempSliceImagesWithTrackList = [] as Array<SliceImage>;
  for (const url of store.currentTask.sliceUrls) {
    for (const config of splitConfigPairs) {
      const indexInSliceUrls = store.currentTask.sliceUrls.indexOf(url) + 1;
      const image = images[indexInSliceUrls - 1];
      let shouldBreak = false;
      if (image.naturalHeight > image.naturalWidth) {
        config[0] = 0;
        config[1] = 1;
        shouldBreak = true;
      }

      accumBottomHeight += image.naturalHeight;

      const dataUrl = await getDataUrlForSplitConfig(
        image,
        config,
        maxSliceWidth,
        url
      );

      let trackLists = [] as Array<Track>;
      if (useMarkResult) {
        trackLists = markResult.trackList;
      } else {
        // 成绩查询 questionList 可能为空
        trackLists = (store.currentTask.questionList || [])
          .map((q) => q.trackList)
          .reduce((acc, t) => {
            acc = acc.concat(t);
            return acc;
          }, [] as Array<Track>);
      }
      const thisImageTrackList = trackLists.filter(
        (t) => t.offsetIndex === indexInSliceUrls
      );

      let tagLists = [] as Array<SpecialTag>;
      if (useMarkResult) {
        tagLists = markResult.specialTagList ?? [];
      } else {
        tagLists = store.currentTask.specialTagList ?? [];
      }
      const thisImageTagList = tagLists.filter(
        (t) => t.offsetIndex === indexInSliceUrls
      );
      const sliceImageRendered = await loadImage(dataUrl!);
      tempSliceImagesWithTrackList.push({
        url: dataUrl!,
        indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url) + 1,
        trackList: thisImageTrackList.filter(
          (t) =>
            t.positionY >= accumTopHeight / theFinalHeight &&
            t.positionY < accumBottomHeight / theFinalHeight
        ),
        tagList: thisImageTagList.filter(
          (t) =>
            t.positionY >= accumTopHeight / theFinalHeight &&
            t.positionY < accumBottomHeight / theFinalHeight
        ),
        // originalImageWidth: image.naturalWidth,
        // originalImageHeight: image.naturalHeight,
        sliceImageWidth: sliceImageRendered.naturalWidth,
        sliceImageHeight: sliceImageRendered.naturalHeight,
        dx: image.naturalWidth * config[0],
        dy: 0,
        accumTopHeight,
        effectiveWidth: image.naturalWidth * config[1],
      });
      accumTopHeight = accumBottomHeight;

      // 如果本图高比宽大，不该裁切，则跳过多次裁切
      if (shouldBreak) {
        break;
      }
    }
  }
  rotateBoard = 1;
  addTimeout(() => {
    sliceImagesWithTrackList.splice(0);
    sliceImagesWithTrackList.push(...tempSliceImagesWithTrackList);
    addTimeout(() => {
      rotateBoard = 0;
    }, 300);
  }, 300);
}

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

  if ((useMarkResult && !markResult) || !store.currentTask) {
    renderLock = false;
    return;
  }

  try {
    store.globalMask = true;

    const hasSliceConfig = store.currentTask?.sliceConfig?.length;

    if (hasSliceConfig) {
      await processSliceConfig();
    } else {
      await processSplitConfig();
    }
    // 研究生考试需要停留在上次阅卷的位置，所以注释掉下面的代码
    // await new Promise((res) => setTimeout(res, 700));
    // const container = <HTMLDivElement>(
    //   document.querySelector(".mark-body-container")
    // );
    // addTimeout(() => {
    //   container?.scrollTo({
    //     top: 0,
    //     left: 0,
    //     behavior: "smooth",
    //   });
    // }, 10);
  } catch (error) {
    sliceImagesWithTrackList.splice(0);
    console.trace("render error ", error);
    // 图片加载出错，自动加载下一个任务
    emit("error");
  } finally {
    renderLock = false;
    store.globalMask = false;
  }
};

// watchEffect(renderPaperAndMark);
// 在阻止渲染的情况下，watchEffect收集不到 store.currentTask 的依赖，会导致本组件不再更新
watch(
  () => store.currentTask,
  () => renderPaperAndMark()
);
// end: 计算裁切图和裁切图上的分数轨迹和特殊标记轨迹

// start: 放大缩小和之后的滚动
let answerPaperScale = $computed(() => {
  // 放大、缩小不影响页面之前的滚动条定位
  let percentWidth = 0;
  let percentTop = 0;
  const container = document.querySelector(
    ".mark-body-container"
  ) as HTMLDivElement;
  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 + "%";
});
// end: 放大缩小和之后的滚动

// start: 显示评分状态和清除轨迹
let markStatus = $ref("");
if (useMarkResult) {
  watch(
    () => store.currentTask,
    () => {
      markStatus = store.getMarkStatus;
    }
  );

  // 清除分数轨迹
  watchEffect(() => {
    for (const track of store.removeScoreTracks) {
      for (const sliceImage of sliceImagesWithTrackList) {
        sliceImage.trackList = sliceImage.trackList.filter(
          (t) =>
            !(
              t.mainNumber === track.mainNumber &&
              t.subNumber === track.subNumber &&
              t.number === track.number
            )
        );
      }
    }
    // 清除后，删除，否则会影响下次切换
    store.removeScoreTracks.splice(0);
  });

  // 清除特殊标记轨迹
  watchEffect(() => {
    for (const sliceImage of sliceImagesWithTrackList) {
      sliceImage.tagList = sliceImage.tagList.filter((t) =>
        store.currentTask?.markResult.specialTagList.find(
          (st) =>
            st.offsetIndex === t.offsetIndex &&
            st.offsetX === t.offsetX &&
            st.offsetY === t.offsetY
        )
      );
    }
    if (store.currentTask?.markResult.specialTagList.length === 0) {
      for (const sliceImage of sliceImagesWithTrackList) {
        sliceImage.tagList = [];
      }
    }
  });
}
// end: 显示评分状态和清除轨迹

// start: 评分
const innerMakeTrack = (event: MouseEvent, item: SliceImage) => {
  makeTrack && makeTrack(event, item, maxSliceWidth, theFinalHeight);
};
// end: 评分

// start: 显示大图，供查看和翻转
const showBigImage = (event: MouseEvent) => {
  event.preventDefault();
  // console.log(event);
  let viewer: Viewer = null as unknown as Viewer;
  viewer && viewer.destroy();
  viewer = new Viewer(event.target as HTMLElement, {
    // inline: true,
    viewed() {
      viewer.zoomTo(1);
    },
    hidden() {
      viewer.destroy();
    },
    zIndex: 1000000,
  });
  viewer.show();
};
// end: 显示大图，供查看和翻转

// onRenderTriggered(({ key, target, type }) => {
//   console.log({ key, target, type });
// });
</script>

<style scoped>
.mark-body-container {
  position: relative;
  min-height: calc(100vh - 56px);
  height: calc(100vh - 56px);
  overflow: auto;
  /* background-size: 8px 8px;
  background-image: linear-gradient(to right, #e7e7e7 4px, transparent 4px),
    linear-gradient(to bottom, transparent 4px, #e7e7e7 4px); */
  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;
}
.mark-body-container img {
  width: 100%;
}
.empty-task {
  width: calc(100%);
  height: calc(100%);
  font-size: 20px;
  overflow: hidden;
  background-color: var(--app-container-bg-color);
}
.empty-task img {
  display: block;
  width: 288px;
  height: 225px;
  clip-path: polygon(0 0, 0 80%, 100% 80%, 100% 0);
}
.single-image-container {
  position: relative;
}
.image-seperator {
  border: 2px solid transparent;
}
.status-container {
  position: fixed;
  top: 56px;
  right: 340px;
  color: white;
  pointer-events: none;
  font-size: var(--app-title-font-size);
  background-color: #ef7c78;

  width: 30px;
  height: 50px;
  text-align: center;
  z-index: 1000;
}
.double-triangle {
  background-color: #ef7c78;
  width: 30px;
  height: 6px;
  clip-path: polygon(0 0, 0 6px, 50% 0, 100% 0, 100% 6px, 50% 0);

  position: absolute;
  bottom: -5px;
}

@keyframes rotate {
  0% {
    transform: rotateY(0deg);
    opacity: 1;
  }
  50% {
    transform: rotateY(90deg);
  }

  100% {
    transform: rotateY(0deg);
    opacity: 0;
  }
}
.rotate-board-1 {
  animation: rotate 0.6s ease-in-out;
}
</style>
