<template>
  <div class="mark-body-container tw-flex-auto tw-p-2" ref="dragContainer">
    <a-spin :spinning="rendering" tip="Loading...">
      <div v-if="!store.currentTask" class="tw-text-center">
        {{ store.message }}
      </div>
      <div v-else :style="{ width: answerPaperScale }">
        <div
          style="
            top: -10px;
            right: 0;
            position: absolute;
            color: red;
            pointer-events: none;
            font-size: 30px;
            z-index: 1000;
          "
        >
          <!-- @mouseover="(e) => (e.target.style.opacity = 0.01)"
          @mouseout="(e) => (e.target.style.opacity = 1)" -->
          {{ markStatus }}
        </div>
        <div
          v-for="(item, index) in sliceImagesWithTrackList"
          :key="index"
          class="single-image-container"
        >
          <img
            :src="item.url"
            @click="(event) => makeScoreTrack(event, item)"
            draggable="false"
          />
          <MarkDrawTrack
            :track-list="item.trackList"
            :original-image="item.originalImage"
            :slice-image="item.sliceImage"
            :dx="item.dx"
            :dy="item.dy"
          />
          <hr class="image-seperator" />
        </div>
      </div>
    </a-spin>
  </div>
  <div class="cursor">
    <div class="cursor-border">
      <span class="text">{{ store.currentScore }}</span>
    </div>
  </div>
</template>

<script lang="ts">
import {
  computed,
  defineComponent,
  onMounted,
  onUnmounted,
  reactive,
  ref,
  watch,
  watchEffect,
} from "vue";
import { findCurrentTaskMarkResult, getMarkStatus, store } from "./store";
import filters from "@/filters";
import MarkDrawTrack from "./MarkDrawTrack.vue";
import { ModeEnum, Track } from "@/types";
import { useTimers } from "@/setups/useTimers";
import {
  getDataUrlForSliceConfig,
  getDataUrlForSplitConfig,
  loadImage,
} from "@/utils/utils";
import { groupBy, sortBy } from "lodash";
// @ts-ignore
import CustomCursor from "custom-cursor.js";

import { dragImage } from "./use/draggable";

interface SliceImage {
  url: string;
  indexInSliceUrls: number;
  trackList: Array<Track>;
  originalImage: HTMLImageElement;
  sliceImage: HTMLImageElement;
  dx: number;
  dy: number;
  accumTopHeight: number;
  effectiveWidth: number;
}
// should not render twice at the same time
let __lock = false;
export default defineComponent({
  name: "MarkBody",
  components: { MarkDrawTrack },
  emits: ["error"],
  setup(props, { emit }) {
    const { dragContainer } = dragImage();

    const { addTimeout } = useTimers();

    function hasSliceConfig() {
      return store.currentTask?.sliceConfig?.length;
    }

    let rendering = ref(false);
    let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
    let _studentId = -1; // 判断是否改变了任务
    let maxSliceWidth = 0; // 最大的裁切块宽度，图片容器以此为准
    let theFinalHeight = 0; // 最终宽度，用来定位轨迹在第几张图片，不包括image-seperator高度

    async function processSliceConfig() {
      // check if have MarkResult for currentTask
      let markResult = findCurrentTaskMarkResult();

      if (!markResult || !store.currentTask) return;

      // 必须要先加载一遍，把“选择整图”的宽高重置后，再算总高度
      for (const sliceConfig of store.currentTask.sliceConfig) {
        const url = filters.toCompleteUrl(
          store.currentTask.sliceUrls[sliceConfig.i - 1]
        );
        const image = await loadImage(url);
        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;
      for (const sliceConfig of store.currentTask.sliceConfig) {
        accumBottomHeight += sliceConfig.h;
        const url = filters.toCompleteUrl(
          store.currentTask.sliceUrls[sliceConfig.i - 1]
        );
        const image = await loadImage(url);

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

        const thisImageTrackList = markResult.trackList.filter(
          (v) => v.offsetIndex === sliceConfig.i
        );
        const sliceImage = new Image();
        sliceImage.src = dataUrl;
        sliceImagesWithTrackList.push({
          url: dataUrl,
          indexInSliceUrls: sliceConfig.i,
          // 通过positionY来定位是第几张slice的还原，并过滤出相应的track
          trackList: thisImageTrackList.filter(
            (t) =>
              t.positionY >= accumTopHeight / theFinalHeight &&
              t.positionY < accumBottomHeight / theFinalHeight
          ),
          originalImage: image,
          sliceImage,
          dx: sliceConfig.x,
          dy: sliceConfig.y,
          accumTopHeight,
          effectiveWidth: sliceConfig.w,
        });
        accumTopHeight = accumBottomHeight;
      }
    }

    async function processSplitConfig() {
      // check if have MarkResult for currentTask
      let markResult = findCurrentTaskMarkResult();

      if (!markResult || !store.currentTask) return;

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

      // TODO: add loading
      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]>;

      const maxSplitConfig = Math.max(...store.setting.splitConfig);
      maxSliceWidth =
        Math.max(...images.map((v) => v.naturalWidth)) * maxSplitConfig;

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

      let accumTopHeight = 0;
      let accumBottomHeight = 0;
      for (const url of store.currentTask.sliceUrls) {
        const completeUrl = filters.toCompleteUrl(url);

        for (const config of splitConfigPairs) {
          const image = await loadImage(completeUrl);

          accumBottomHeight += image.naturalHeight;

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

          const thisImageTrackList = markResult.trackList.filter(
            (t) =>
              t.offsetIndex ===
              (store.currentTask &&
                store.currentTask.sliceUrls.indexOf(url) + 1)
          );
          const sliceImage = new Image();
          sliceImage.src = dataUrl;
          sliceImagesWithTrackList.push({
            url: dataUrl,
            indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url) + 1,
            trackList: thisImageTrackList.filter(
              (t) =>
                t.positionY >= accumTopHeight / theFinalHeight &&
                t.positionY < accumBottomHeight / theFinalHeight
            ),
            originalImage: image,
            sliceImage,
            dx: image.naturalWidth * config[0],
            dy: 0,
            accumTopHeight,
            effectiveWidth: image.naturalWidth * config[1],
          });
          accumTopHeight = accumBottomHeight;
        }
      }
    }
    // 供回退和清除使用
    // let trackLen = store.currentMarkResult?.trackList.length;
    const renderPaperAndMark = async () => {
      if (__lock) {
        await new Promise((res) => setTimeout(res, 1000));
        await renderPaperAndMark();
      }
      __lock = true;
      sliceImagesWithTrackList.splice(0);
      // check if have MarkResult for currentTask
      let markResult = findCurrentTaskMarkResult();
      // if (!store.currentTask) {
      //   sliceImagesWithTrackList.splice(0);
      //   _studentId = -1;
      // }

      if (!markResult || !store.currentTask) {
        __lock = false;
        return;
      }

      // reset sliceImagesWithTrackList ，当切换任务时，要重新绘制图片和轨迹
      // if (_studentId !== store.currentTask.studentId) {
      //   // 还原轨迹用得上
      //   sliceImagesWithTrackList.splice(0);
      //   _studentId = store.currentTask.studentId;
      // }

      try {
        rendering.value = true;
        if (hasSliceConfig()) {
          await processSliceConfig();
        } else {
          await processSplitConfig();
        }
      } catch (error) {
        sliceImagesWithTrackList.splice(0);
        console.log("render error ", error);
        // 图片加载出错，自动加载下一个任务
        emit("error");
      } finally {
        __lock = false;
        rendering.value = false;
      }
    };

    watchEffect(renderPaperAndMark);

    const 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 + "%";
    });

    const makeScoreTrack = (event: MouseEvent, item: SliceImage) => {
      // console.log(item);
      if (!store.currentQuestion || typeof store.currentScore === "undefined")
        return;
      const target = event.target as HTMLImageElement;
      const track = {} as Track;
      track.mainNumber = store.currentQuestion?.mainNumber;
      track.subNumber = store.currentQuestion?.subNumber;
      // track.number = (Date.now() - new Date(2021, 0, 0).valueOf()) / 10e7;
      track.score = store.currentScore;
      track.offsetIndex = item.indexInSliceUrls;
      track.offsetX = Math.round(
        event.offsetX * (target.naturalWidth / target.width) + item.dx
      );
      track.offsetY = Math.round(
        event.offsetY * (target.naturalHeight / target.height) + item.dy
      );
      track.positionX = (track.offsetX - item.dx) / maxSliceWidth;
      track.positionY =
        (track.offsetY - item.dy + item.accumTopHeight) / theFinalHeight;
      if (track.offsetX > item.effectiveWidth + item.dx) {
        console.log("不在有效宽度内，轨迹不生效");
        return;
      }
      if (
        item.trackList.some((t) => {
          return (
            Math.pow(Math.abs(t.offsetX - track.offsetX), 2) +
              Math.pow(Math.abs(t.offsetY - track.offsetY), 2) <
            500
          );
        })
      ) {
        console.log("两个轨迹相距过近");
        return;
      }
      // 是否保留当前的轨迹分
      const ifKeepScore =
        Math.round(
          store.currentQuestion.maxScore * 100 -
            (store.currentQuestion.score || 0) * 100 -
            store.currentScore * 2 * 100
        ) / 100;
      if (
        (ifKeepScore < 0 && store.currentScore > 0) ||
        Math.round(ifKeepScore * 100) %
          Math.round(store.currentQuestion.intervalScore * 100) !==
          0
      ) {
        store.currentScore = undefined;
      }
      const markResult = findCurrentTaskMarkResult();
      if (markResult) {
        const maxNumber =
          markResult.trackList.length === 0
            ? 0
            : Math.max(...markResult.trackList.map((t) => t.number));
        track.number = maxNumber + 1;
        // console.log(
        //   maxNumber,
        //   track.number,
        //   markResult.trackList.map((t) => t.number),
        //   Math.max(...markResult.trackList.map((t) => t.number))
        // );
        markResult.trackList = [...markResult.trackList, track];
      }
      item.trackList.push(track);
    };

    // 清除分数轨迹
    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
              )
          );
        }
      }
    });

    // 轨迹模式下，添加轨迹，更新分数
    watch(
      () => store.currentMarkResult?.trackList,
      () => {
        if (store.setting.mode !== ModeEnum.TRACK) return;
        const markResult = findCurrentTaskMarkResult();
        if (markResult && store.currentMarkResult) {
          const scoreGroups = groupBy(
            markResult.trackList,
            (obj) =>
              (obj.mainNumber + "").padStart(10, "0") +
              obj.subNumber.padStart(10, "0")
          );
          const questionWithScore = Object.entries(scoreGroups);
          const questionWithTotalScore = questionWithScore.map((v) => [
            v[0],
            v[1]
              .map((s) => Math.round(s.score * 100))
              .reduce((acc, c) => (acc += c), 0) / 100,
          ]);
          const questionWithTotalScoreSorted = sortBy(
            questionWithTotalScore,
            (obj) => obj[0]
          );
          const scoreList = questionWithTotalScoreSorted.map((s) => s[1]);
          // console.log(
          //   scoreGroups,
          //   questionWithScore,
          //   questionWithTotalScore,
          //   questionWithTotalScoreSorted,
          //   scoreList
          // );
          const cq = store.currentQuestion;
          // 当无轨迹时，不更新
          if (cq && markResult.trackList.length > 0) {
            cq.score =
              markResult.trackList
                .filter(
                  (v) =>
                    v.mainNumber === cq.mainNumber &&
                    v.subNumber === cq.subNumber
                )
                .map((v) => v.score)
                .reduce((acc, v) => (acc += Math.round(v * 100)), 0) / 100;
          }
          markResult.scoreList = scoreList as number[];
          // const sortScore = orderBy(markResult.trackList, ['mainNumber', 'subNumber', 'score']);
          // markResult.scoreList = sortScore.reduce((acc, pre) => {
          //   if(pre.mainNumber === cur.mainNumber && pre.subNumber === cur.subNumber) {
          //     acc[acc.length-1] += cur.score
          //   }
          // }, [0])
          markResult.markerScore =
            markResult.scoreList
              .filter((v): v is number => v !== null)
              .reduce((acc, v) => (acc += Math.round(v * 100)), 0) / 100;

          // console.log(markResult.scoreList, markResult.markerScore);
          // renderPaperAndMark();
        }
      },
      { deep: true }
    );

    watch(
      () => store.setting.mode,
      () => {
        const shouldHide = store.setting.mode === ModeEnum.COMMON;
        if (shouldHide) {
          // console.log("hide cursor", theCursor);
          theCursor && theCursor.destroy();
        } else {
          if (document.querySelector(".cursor")) {
            // console.log("show cursor", theCursor);
            // theCursor && theCursor.enable();
            theCursor = new CustomCursor(".cursor", {
              focusElements: [
                {
                  selector: ".mark-body-container",
                  focusClass: "cursor--focused-view",
                },
              ],
            }).initialize();
          }
        }
      }
    );
    let theCursor = null as any;
    onMounted(() => {
      if (store.setting.mode === ModeEnum.TRACK) {
        theCursor = new CustomCursor(".cursor", {
          focusElements: [
            {
              selector: ".mark-body-container",
              focusClass: "cursor--focused-view",
            },
          ],
        }).initialize();
      }
    });

    onUnmounted(() => {
      theCursor && theCursor.destroy();
    });

    const markStatus = ref("");
    watch(
      () => store.currentTask,
      () => {
        markStatus.value = getMarkStatus();
      }
    );

    return {
      dragContainer,
      store,
      rendering,
      sliceImagesWithTrackList,
      answerPaperScale,
      makeScoreTrack,
      markStatus,
    };
  },
  // renderTriggered({ key, target, type }) {
  //   console.log({ key, target, type });
  // },
});
</script>

<style scoped>
.mark-body-container {
  height: calc(100vh - 41px);
  overflow: scroll;
  background-size: 8px 8px;
  background-image: linear-gradient(to right, #e7e7e7 4px, transparent 4px),
    linear-gradient(to bottom, transparent 4px, #e7e7e7 4px);
}
.mark-body-container img {
  width: 100%;
}
.single-image-container {
  position: relative;
}
.image-seperator {
  border: 2px solid rgba(120, 120, 120, 0.1);
}

.hide-cursor {
  display: none !important;
}

.cursor {
  color: #ff5050;
  display: none;
  pointer-events: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  top: 0;
  left: 0;
  position: fixed;
  will-change: transform;
  z-index: 1000;
}

.cursor-border {
  position: absolute;
  box-sizing: border-box;
  align-items: center;
  border: 1px solid #ff5050;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  height: 0px;
  width: 0px;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
  transition: all 360ms cubic-bezier(0.23, 1, 0.32, 1);
}

.cursor.cursor--initialized {
  display: block;
}

.cursor .text {
  font-size: 2rem;
  opacity: 0;
  transition: opacity 80ms cubic-bezier(0.23, 1, 0.32, 1);
}

.cursor.cursor--off-screen {
  opacity: 0;
}

.cursor.cursor--focused .cursor-border,
.cursor.cursor--focused-view .cursor-border {
  width: 90px;
  height: 90px;
}

.cursor.cursor--focused-view .text {
  opacity: 1;
  transition: opacity 360ms cubic-bezier(0.23, 1, 0.32, 1);
}
</style>
