<template>
  <div v-if="!dataError" class="tw-h-screen confirm-paper">
    <header
      class="tw-flex tw-gap-2 tw-justify-between tw-items-center header-container"
    >
      <div class="tw-ml-2">
        进度：<span class="highlight-text">
          {{ currentIndex }}/{{ allIds.length }}
        </span>
      </div>
      <div>
        姓名：<span class="highlight-text">{{ student?.name }}</span>
      </div>
      <div>
        准考证号：<span class="highlight-text">{{ student?.examNumber }}</span>
      </div>
      <div>
        学号：<span class="highlight-text">{{ student?.studentCode }}</span>
      </div>
      <div>
        科目：<span class="highlight-text">
          {{ student?.subjectCode }}-{{ student?.subjectName }}
        </span>
      </div>
      <div>
        客观分：<span class="highlight-text">{{
          student?.objectiveScore
        }}</span>
      </div>
      <div>
        主观分：<span class="highlight-text">{{
          student?.subjectiveScore
        }}</span>
      </div>
      <div class="tw-flex tw-items-center tw-gap-2 tw-mx-8">
        <span
          v-for="(u, index) in student?.sheetUrls"
          :key="index"
          class="tw-cursor-pointer"
          :class="currentImage === index && 'highlight-text'"
          @click="currentImage = index"
        >
          {{ index + 1 }}
        </span>
      </div>
    </header>

    <div class="tw-flex" style="height: calc(100% - 56px)">
      <div
        style="flex: 0 1 420px; overflow: auto"
        class="tw-flex tw-flex-col tw-justify-between"
      >
        <div class="tw-m-2 tw-flex-1 tw-overflow-auto">
          <div v-if="pageType === 'DATA_CHECK'" class="tw-my-2 top-block">
            是否缺考：
            <a-radio-group v-if="student" v-model:value="student.absent">
              <a-radio :value="true">是</a-radio>
              <a-radio :value="false">否</a-radio>
            </a-radio-group>
          </div>
          <div v-if="pageType === 'DATA_CHECK'" class="tw-my-2 top-block">
            试卷类型：
            <a-input
              v-if="student"
              v-model:value="student.paperType"
              class="normal-input"
              :maxlength="1"
            />
          </div>

          <div v-if="student?.answers" class="tw-mt-4">
            <div
              v-for="group in answersComputed"
              :key="group.mainNumber"
              class="tw-mt-2"
              :class="{ hide: !group.subs?.filter((s) => s.exist).length }"
            >
              <h2>
                {{ group.mainNumber }}、{{ group.mainTitle }} ({{
                  group.subs.length
                }})
              </h2>
              <div class="tw-flex tw-flex-wrap tw-gap-2">
                <div
                  v-for="question in group.subs"
                  :key="question.subNumber"
                  :class="{ hide: !question.exist }"
                >
                  <span class="question-number">{{ question.subNumber }} </span>
                  <!-- <a-input
                    class="normal-input"
                    :class="{
                      'long-input': question.type
                        ? ['SINGLE', 'MULTIPLE'].includes(question.type)
                        : group.mainTitle.match(/单选|多选|多项|不定项/),
                      'middle-input': question.type
                        ? ['TRUE_OR_FALSE'].includes(question.type)
                        : group.mainTitle.match(/判断/),
                    }"
                    :value="question.answer"
                    :maxLength="
                      (
                        question.type
                          ? ['MULTIPLE'].includes(question.type)
                          : group.mainTitle.match(/多选|多项|不定项/)
                      )
                        ? 100
                        : 1
                    "
                    @keydown="onPreventAnswerKey"
                    @input="changeAnswer($event, question)"
                    @blur="changeAnswer($event, question, '#')"
                  /> -->
                  <a-input
                    class="normal-input"
                    :class="{
                      'long-input': question.type
                        ? !['SINGLE', 'TRUE_OR_FALSE'].includes(question.type)
                        : !group.mainTitle.match(/单选|单项|判断/),
                    }"
                    :value="question.answer"
                    :maxLength="
                      (
                        question.type
                          ? ['MULTIPLE'].includes(question.type)
                          : group.mainTitle.match(/多选|多项|不定项/)
                      )
                        ? 100
                        : 1
                    "
                    @keydown="onPreventAnswerKey"
                    @input="changeAnswer($event, question)"
                    @blur="changeAnswer($event, question, '#')"
                  />
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="tw-flex tw-justify-between tw-bg-white tw-p-4">
          <a-button
            :disabled="!student?.upload"
            type="primary"
            shape="round"
            @click="saveStudentAnswer"
          >
            保存
          </a-button>
          <div>
            <a-button
              shape="round"
              :disabled="currentIndex <= 1"
              class="tw-mr-4"
              @click="getPreviousStudent"
            >
              上一份
            </a-button>
            <a-button
              shape="round"
              :disabled="currentIndex === allIds.length"
              @click="getNextStudent"
            >
              下一份
            </a-button>
          </div>
        </div>
      </div>

      <div style="flex: 1" class="mark-body-container tw-relative">
        <ArrowLeftOutlined
          v-if="student && currentImage !== 0"
          class="tw-cursor-pointer tw-absolute"
          style="top: 45%; left: 20px; z-index: 1; font-size: 40px"
          title="上一张"
          @click="switchImageArrow({ left: true })"
        />
        <ArrowRightOutlined
          v-if="student && currentImage !== student.sheetUrls.length - 1"
          class="tw-cursor-pointer tw-absolute"
          style="top: 45%; right: 20px; z-index: 1; font-size: 40px"
          title="上一张"
          @click="switchImageArrow({ right: true })"
        />
        <div :style="{ width: answerPaperScale }">
          <img
            v-for="(item, index) in student?.sheetUrls"
            :key="item"
            class="tw-object-cover"
            :src="item"
            :style="{
              display: index === currentImage ? 'block' : 'none',
              transform:
                (rotateDegree ? 'translate( 0,  calc(30vh))' : '') +
                `rotate(${rotateDegree}deg)`,
            }"
            @click="switchImage"
            @contextmenu="showBigImage"
          />
        </div>
        <ZoomPaper v-if="student" fixed showRotate @rotateRight="rotateRight" />
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { httpApp } from "@/plugins/axiosApp";
import { message } from "ant-design-vue";
import { onMounted, reactive } from "vue";
import { useRoute } from "vue-router";
import { CheckSetting, StudentInfo } from "./check";
import "viewerjs/dist/viewer.css";
import Viewer from "viewerjs";
import { store } from "@/store/store";
import ZoomPaper from "@/components/ZoomPaper.vue";
import { useTimers } from "@/setups/useTimers";
import { ArrowLeftOutlined, ArrowRightOutlined } from "@ant-design/icons-vue";

const { addTimeout } = useTimers();

const route = useRoute();
// 使用 location.search 替代
// const checkType = route.query.checkType;
const queryId = route.query.queryId as string;
let pageType: "DATA_CHECK" | "HAND_CHECK" = "HAND_CHECK";
if (queryId) {
  pageType = "DATA_CHECK";
  sessionStorage.setItem(queryId, localStorage.getItem(queryId) || "[]");
  localStorage.removeItem(queryId);
}

onMounted(async () => {
  await getSetting();
  if (setting.studentIds.length === 0) {
    void message.info("没有需要处理的考生，请返回。");
    return;
  }
  await getNextStudent();
});

let setting: CheckSetting = reactive({
  fileServer: "",
  studentIds: [],
  studentIdsDone: [],
});

const allIds = $computed(() => [
  ...setting.studentIdsDone,
  ...setting.studentIds,
]);

let currentStudentId = $ref(-1);

const currentIndex = $computed(() => allIds.indexOf(currentStudentId) + 1);

let student: StudentInfo | null = $ref(null);
/** 后台数据错误，停止整个页面的流程 */
let dataError = $ref(false);

const answersComputed = $computed(() => {
  let mains = student?.answers.map((v) => ({
    mainTitle: "",
    mainNumber: v.mainNumber,
    subs: [v],
  }));
  const mSet = new Set();
  mains = mains?.filter((v) => {
    if (!mSet.has(v.mainNumber)) {
      mSet.add(v.mainNumber);
      v.subs = [];
      return true;
    }
  });
  mains?.forEach((v) => {
    v.mainTitle = student?.titles[v.mainNumber] ?? "";
    v.subs =
      student?.answers.filter((v2) => v2.mainNumber === v.mainNumber) ?? [];
  });
  // console.log(mains);
  return mains;
});

async function getSetting() {
  let res: any;
  if (pageType === "DATA_CHECK") {
    const query: Array<{ name: string; value: string }> = JSON.parse(
      sessionStorage.getItem(queryId) || "[]"
    );
    const form = new FormData();
    for (const v of query) {
      form.append(v.name, v.value + "");
    }
    res = await httpApp.post("/admin/exam/check/answer/getSetting", form);
  } else {
    const form = new FormData();
    form.append("checkType", route.query.checkType as string);
    form.append("subjectCode", route.query.subjectCode as string);
    form.append("examSite", route.query.examSite as string);
    res = await httpApp.post(`/admin/exam/check/student/getSetting`, form);
  }

  setting.fileServer = res.data.fileServer;
  setting.studentIds = res.data.studentIds || [];
}

async function getNextStudent() {
  const wantedIndex = allIds.indexOf(currentStudentId);
  if (allIds[wantedIndex + 1]) {
    student = await getStudent(allIds[wantedIndex + 1]);
  }
}

async function getPreviousStudent() {
  const wantedIndex = allIds.indexOf(currentStudentId);
  student = await getStudent(allIds[wantedIndex - 1]);
}

async function getStudent(studentId: number) {
  const stu: StudentInfo = await (
    await httpApp.get(`/admin/exam/check/answer/info?studentId=${studentId}`)
  ).data;
  stu?.sheetUrls.forEach((v, i, a) => (a[i] = setting.fileServer + v));
  currentStudentId = stu.id;
  currentImage = 0;

  if (!stu.success) {
    void message.error(stu.message, 24 * 60 * 60);
    dataError = true;

    throw new Error("取学生信息出错: " + stu.message);
  }

  // for dev
  // stu.answers = [
  //   { mainNumber: 1, subNumber: "1", answer: "A" },
  //   { mainNumber: 1, subNumber: "2", answer: "B" },
  //   { mainNumber: 2, subNumber: "1", answer: "#" },
  // ];
  // stu.titles = { 1: "单选题", 2: "多选题" };
  return stu;
}

const allowKey = [
  "Delete",
  "Backspace",
  "ArrowLeft",
  "ArrowRight",
  "#",
  "Shift",
  "[A-Za-z]",
];
const allowKeyRef = new RegExp(allowKey.join("|"));

function onPreventAnswerKey(e: KeyboardEvent) {
  console.log(e);
  if (!allowKeyRef.test(e.key)) {
    e.preventDefault();
  }
}

function changeAnswer(
  event: Event,
  question: StudentInfo["answers"][0],
  defaultValue?: string
) {
  const target = event.target as HTMLInputElement;
  student.answers = student.answers.map((v) => {
    if (
      v.mainNumber === question.mainNumber &&
      v.subNumber === question.subNumber
    ) {
      v.answer = target?.value.toUpperCase().trim() || defaultValue || "";
    }
    return v;
  });
}

async function saveStudentAnswer() {
  if (!student) return;
  const form = new FormData();
  form.append("studentId", student.id + "");
  const answers = student.answers.map((v) => v.answer || "#").join(",");
  // if (!answers.match(/^(#*,*[A-Z]*)+$/g)) {
  //   void message.error("答案只能是#和大写英文字母");
  //   return;
  // }

  form.append("answers", answers);

  const extra = pageType === "DATA_CHECK";
  extra && form.append("absent", student.absent + "");
  extra && form.append("paperType", student.paperType);
  if (extra) {
    if (!student.paperType.match(/^#|[A-Z]$/)) {
      void message.error("试卷类型只能是#和大写英文字母");
      return;
    }
  }

  const url = extra
    ? // 数据检查
      "/admin/exam/check/answer/save"
    : `/admin/exam/check/student/save`;
  const res = await httpApp
    .post(url, form)
    .catch(() => message.error("保存失败-接口调用失败"));
  if (!res.data) {
    void message.error("保存失败，请刷新页面。");
  } else {
    void message.success("保存成功");
    await getNextStudent();
  }

  if (setting.studentIds.length === 0) {
    void message.success("所有考生已处理完毕。");
  }
}

//#region : 显示大图，供查看和翻转
let currentImage = $ref(0);
function switchImageArrow({
  left = false,
  right = false,
}: {
  left?: boolean;
  right?: boolean;
}) {
  if (left) {
    if (currentImage > 0) {
      currentImage--;
    }
  }
  if (right) {
    if (currentImage < student.sheetUrls.length - 1) {
      currentImage++;
    }
  }
}

function switchImage(event: MouseEvent) {
  const image = event.target as HTMLImageElement;
  const layerX: number = (event as any).layerX;
  if (layerX * 2 < image.width) {
    if (currentImage > 0) {
      currentImage--;
    }
  } else {
    if (currentImage < student.sheetUrls.length - 1) {
      currentImage++;
    }
  }
}

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).parentElement, {
    // inline: true,
    viewed() {
      viewer.zoomTo(1);
    },
    hidden() {
      viewer.destroy();
    },
    zIndex: 1000000,
  });
  viewer.show();
};
//#endregion : 显示大图，供查看和翻转

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

//#region rotateRight
let rotateDegree = $ref(0);
function rotateRight() {
  rotateDegree = (rotateDegree + 90) % 360;
}
//#endregion
</script>

<style scoped>
.question-number {
  display: inline-block;
  width: 22px;
  text-align: right;
  font-size: 12px;
  font-weight: normal;
  color: #474966;
  line-height: 14px;
  margin-right: 0.5em;
}
.normal-input {
  width: 36px;
  height: 32px;
  background: #edf2fa;
  border-radius: 6px;
  border: 1px solid #c8ced8;
  font-size: 14px;
  font-weight: normal;
  color: #474966;
  text-align: center;
}
.normal-input.long-input {
  width: 94px;
  padding: 4px;
}
.normal-input.middle-input {
  width: 61px !important;
  padding: 4px;
}
.header-container {
  position: relative;
  height: 56px;
  line-height: 16px;

  background-color: var(--header-bg-color);
  color: rgba(255, 255, 255, 0.5);
}
.highlight-text {
  color: white;
  font-size: var(--app-title-font-size);
}

.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;
}

.hide {
  display: none;
  visibility: hidden;
  opacity: 0;
}
.confirm-paper .top-block {
  background-color: #fff;
  height: 50px;
  padding: 0 20px;
  display: flex;
  align-items: center;
  border-radius: 6px;
}
</style>
