<template>
  <div class="mark-body" @scroll="viewScroll">
    <div ref="dragContainer" class="mark-body-container">
      <div v-if="!store.currentTask" class="mark-body-none">
        <div>
          <img src="@/assets/image-none-task.png" />
          <p>
            {{ store.message }}
          </p>
        </div>
      </div>
      <div v-else-if="store.isScanImage" :style="{ width: answerPaperScale }">
        <div
          v-for="(item, index) in sliceImagesWithTrackList"
          :key="index"
          class="single-image-container"
        >
          <img :src="item.url" draggable="false" />
          <MarkDrawTrack
            :trackList="item.trackList"
            :specialTagList="item.tagList"
            :sliceImageHeight="item.originalImageHeight"
            :sliceImageWidth="item.originalImageWidth"
            :dx="0"
            :dy="0"
          />
          <!-- 客观题答案标记 -->
          <template v-if="item.answerTags">
            <div
              v-for="(tag, tindex) in item.answerTags"
              :key="`tag-${tindex}`"
              :style="tag.style"
            >
              {{ tag.answer }}
            </div>
          </template>
          <!-- 试题评分明细 -->
          <template v-if="item.markDetail">
            <div
              v-for="(minfo, mindex) in item.markDetail"
              :key="`mark-${mindex}`"
              :style="minfo.style"
              class="mark-info"
            >
              <div>
                <p v-for="user in minfo.users" :key="user.userId">
                  评卷员：{{ user.userName }}，评分：{{ user.score }}
                </p>
              </div>
              <h3>得分：{{ minfo.score }}，满分：{{ minfo.maxScore }}</h3>
            </div>
          </template>
          <hr class="image-seperator" />
        </div>
      </div>
      <div v-else>未知数据</div>
    </div>
  </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, PaperRecogData } from "@/types";
import { useTimers } from "@/setups/useTimers";
import { loadImage, addHeaderTrackColorAttr, calcSum } from "@/utils/utils";
import { dragImage } from "@/features/mark/use/draggable";
import { maxNum } from "@/utils/utils";

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

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.userId)
    .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 + ""] = "gray";
    }
  }
  if (Object.keys(colorMap).length > 1) {
    emit("getIsMultComments", true);
  }
  tList = tList.map((item: Track) => {
    item.color = colorMap[item.userId + ""] || "red";
    item.isByMultMark = markerIds.length > 1;
    return item;
  });
  return tList;
}

function addTagColorAttr(tList: SpecialTag[]): SpecialTag[] {
  let markerIds: (number | undefined)[] = tList
    .map((v) => v.userId)
    .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 + ""] = "gray";
    }
  }
  tList = tList.map((item: SpecialTag) => {
    item.color = colorMap[item.userId + ""] || "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));

  // 解析各试题答题区域以及评分
  const markDetailList = parseMarkDetailList();

  const trackLists = (store.currentTask.questionList || [])
    // .map((q) => q.trackList)
    .map((q) => {
      let tList = q.trackList;
      return q.headerTrack?.length
        ? addHeaderTrackColorAttr(q.headerTrack)
        : addTrackColorAttr(tList);
    })
    .flat();
  store.setting.doubleTrack = trackLists.some((item) => item.isByMultMark);

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

    const thisImageTrackList = trackLists.filter(
      (t) => t.offsetIndex === indexInSliceUrls
    );
    const thisImageTagList = store.currentTask.headerTagList?.length
      ? addHeaderTrackColorAttr(
          (store.currentTask.headerTagList || []).filter(
            (t) => t.offsetIndex === indexInSliceUrls
          )
        )
      : addTagColorAttr(
          (store.currentTask.specialTagList || []).filter(
            (t) => t.offsetIndex === indexInSliceUrls
          )
        );
    const answerTags = paserRecogData(image, indexInSliceUrls - 1);

    sliceImagesWithTrackList.push({
      url,
      trackList: thisImageTrackList,
      tagList: thisImageTagList,
      originalImageWidth: image.naturalWidth,
      originalImageHeight: image.naturalHeight,
      width: (image.naturalWidth / maxImageWidth) * 100 + "%",
      answerTags,
      markDetail: markDetailList[indexInSliceUrls - 1],
    });
  }
}

// 解析客观题答案展示位置
interface AnswerTagItem {
  mainNumber: number;
  subNumber: string;
  answer: string;
  style: Record<string, string>;
}
function paserRecogData(imgDom: HTMLImageElement, imageIndex): AnswerTagItem[] {
  if (
    !store.currentTask.recogDatas?.length ||
    !store.currentTask.recogDatas[imageIndex]
  )
    return [];

  const answerMap = store.currentTask.answerMap || {};
  const { naturalWidth, naturalHeight } = imgDom;
  const recogData: PaperRecogData = JSON.parse(
    window.atob(store.currentTask.recogDatas[imageIndex])
  );
  const answerTags: AnswerTagItem[] = [];
  // const optionsBlocks = [];
  recogData.question.forEach((question) => {
    question.fill_result.forEach((result) => {
      const tagSize = result.fill_size[1];
      const fillPositions = result.fill_position.map((pos) => {
        return pos.split(",").map((n) => n * 1);
      });

      const offsetLt = result.fill_size.map((item) => item * 0.4);
      const tagLeft =
        maxNum(fillPositions.map((pos) => pos[0])) +
        result.fill_size[0] -
        offsetLt[0];
      const tagTop = fillPositions[0][1] - offsetLt[1];

      answerTags.push({
        mainNumber: result.main_number,
        subNumber: result.sub_number,
        answer: answerMap[`${result.main_number}_${result.sub_number}`],
        style: {
          height: ((100 * tagSize) / naturalHeight).toFixed(4) + "%",
          fontSize: ((100 * 20) / tagSize).toFixed(4) + "%",
          left: ((100 * tagLeft) / naturalWidth).toFixed(4) + "%",
          top: ((100 * tagTop) / naturalHeight).toFixed(4) + "%",
          position: "absolute",
          color: "#f53f3f",
          lineHeight: 1,
          zIndex: 9,
        },
      });

      // 测试：选项框
      // fillPositions.forEach((fp, index) => {
      //   optionsBlocks.push({
      //     mainNumber: result.main_number,
      //     subNumber: result.sub_number,
      //     filled: !!result.fill_option[index],
      //     style: {
      //       width:
      //         ((100 * result.fill_size[0]) / naturalWidth).toFixed(4) + "%",
      //       height:
      //         ((100 * result.fill_size[1]) / naturalHeight).toFixed(4) + "%",
      //       left:
      //         ((100 * (fp[0] - offsetLt[0])) / naturalWidth).toFixed(4) + "%",
      //       top:
      //         ((100 * (fp[1] - offsetLt[1])) / naturalHeight).toFixed(4) + "%",
      //       position: "absolute",
      //       border: "1px solid #f53f3f",
      //       background: result.fill_option[index]
      //         ? "rgba(245, 63, 63, 0.5)"
      //         : "transparent",
      //       zIndex: 9,
      //     },
      //   });
      // });
    });
  });

  return answerTags;
}

interface QuestionItem {
  mainNumber: number;
  subNumber: number | string;
}
interface QuestionArea {
  i: number;
  x: number;
  y: number;
  w: number;
  h: number;
  qStruct: string;
}
function parseQuestionAreas(questions: QuestionItem[]) {
  if (!questions.length || !store.currentTask.cardData?.length) return [];

  let pictureConfigs: QuestionArea[] = [];
  const structs = questions.map(
    (item) => `${item.mainNumber}_${item.subNumber}`
  );
  store.currentTask.cardData.forEach((page, pindex) => {
    page.exchange.answer_area.forEach((area) => {
      const [x, y, w, h] = area.area;
      const qStruct = `${area.main_number}_${area.sub_number}`;

      const pConfig: QuestionArea = {
        i: pindex + 1,
        x,
        y,
        w,
        h,
        qStruct,
      };

      if (typeof area.sub_number === "number") {
        if (!structs.includes(qStruct)) return;
        pictureConfigs.push(pConfig);
        return;
      }
      // 复合区域处理，比如填空题，多个小题合并为一个区域
      if (typeof area.sub_number === "string") {
        const areaStructs = area.sub_number
          .split(",")
          .map((subNumber) => `${area.main_number}_${subNumber}`);
        if (
          structs.some((struct) => areaStructs.includes(struct)) &&
          !pictureConfigs.find((item) => item.qStruct === qStruct)
        ) {
          pictureConfigs.push(pConfig);
        }
      }
    });
  });
  // console.log(pictureConfigs);

  // 合并相邻区域
  pictureConfigs.sort((a, b) => {
    return a.i - b.i || a.x - b.x || a.y - b.y;
  });
  let combinePictureConfigList: QuestionArea[] = [];
  let prevConfig = null;
  pictureConfigs.forEach((item, index) => {
    if (!index) {
      prevConfig = { ...item };
      combinePictureConfigList.push(prevConfig);
      return;
    }

    const elasticRate = 0.01;
    if (
      prevConfig.i === item.i &&
      prevConfig.y + prevConfig.h + elasticRate >= item.y &&
      prevConfig.w === item.w &&
      prevConfig.x === item.x
    ) {
      prevConfig.h = item.y + item.h - prevConfig.y;
    } else {
      prevConfig = { ...item };
      combinePictureConfigList.push(prevConfig);
    }
  });
  // console.log(combinePictureConfigList);
  return combinePictureConfigList;
}

// 解析各试题答题区域以及评分
interface MarkDetailItem {
  mainNumber: number;
  subNumber: string;
  score: number;
  maxScore: number;
  users: Array<{
    userId: string;
    userName: string;
    scores: number[];
    score: number;
  }>;
  area: QuestionArea;
  style: Record<string, string>;
}
function parseMarkDetailList(): Array<MarkDetailItem[]> {
  const dataList: Array<MarkDetailItem[]> = [];

  (store.currentTask.questionList || []).forEach((question) => {
    const areas = parseQuestionAreas([question]);
    if (!areas.length) return;
    const area = areas[0];
    if (!dataList[area.i - 1]) {
      dataList[area.i - 1] = [];
    }

    const userMap = {};
    question.trackList.forEach((track) => {
      if (!userMap[track.userId]) {
        userMap[track.userId] = {
          userId: track.userId,
          userName: track.userName,
          scores: [],
        };
      }
      userMap[track.userId].scores.push(track.score);
    });

    const users = Object.values(userMap).map((user) => {
      return { ...user, score: calcSum(user.scores) };
    });

    dataList[area.i - 1].push({
      mainNumber: question.mainNumber,
      subNumber: question.subNumber,
      score: question.score,
      maxScore: question.maxScore,
      users,
      area,
      style: {
        position: "absolute",
        left: (100 * area.x).toFixed(4) + "%",
        top: (100 * area.y).toFixed(4) + "%",
        width: (100 * area.w).toFixed(4) + "%",
        color: "#f53f3f",
        fontSize: "14px",
        lineHeight: 1,
        zIndex: 9,
      },
    });
  });

  return dataList;
}

// 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 {
  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);
}
.mark-info {
  display: flex;
  justify-content: space-between;
}
.mark-info h3 {
  font-size: 20px;
  font-weight: bold;
  line-height: 1;
  color: #f53f3f;
}
.mark-info p {
  margin: 0;
  line-height: 20px;
  font-weight: bold;
}
</style>
