<template>
  <div class="title title_grey cl">
    <h2>图片下载中 …</h2>
  </div>
  <div class="progress-box">
    <h3>正在下载图片，请耐心等候 ~</h3>
    <div class="progress">
      <a-progress :percent="Math.round((finishedCount / totalCount) * 100)" />
    </div>
    <p>
      已下载图片：<b>{{ finishedCount }}</b> / 错误图片：<b>{{ errorCount }}</b>
      / 全部图片：<b>{{ totalCount }}</b>
    </p>
  </div>
</template>

<script setup lang="ts">
import { store } from "@/store";
import { onMounted, onUnmounted, ref } from "vue";
import { Modal } from "ant-design-vue";
import { useRouter } from "vue-router";
const router = useRouter();
import { countStudents, getPackages, getStudents } from "@/api/api";
import { httpApp } from "@/plugins/axiosApp";
import mustache from "mustache";

async function getImageDim(
  blob: Blob
): Promise<[width: number, height: number]> {
  return new Promise((res) => {
    const img = new Image();
    img.src = URL.createObjectURL(blob);
    img.onload = () => {
      URL.revokeObjectURL(img.src);
      // console.log(img.width);
      res([img.width, img.height]);
    };
  });
}

// cache images
async function cacheImages(urls: string) {
  let allPromiseCount = 0;
  let settledPromiseCount = 0;
  const MAX_CONCURRENT = 6;

  async function sleep() {
    if (allPromiseCount - settledPromiseCount >= MAX_CONCURRENT) {
      console.log("sleep because cache images thread is full");
      await new Promise((res) => setTimeout(res, 300));
      await sleep();
    }
  }
  for (const url of urls) {
    allPromiseCount++;
    await sleep();
    // console.log(url);
    httpApp
      .get(url, {
        responseType: "blob",
        headers: {
          // 怀疑 electron 有问题，这里没生效
          "Cache-Control": "no-cache",
        },
      })
      .then(() => console.debug("downloaded ", url))
      .catch((e) => {
        console.log(e, "cache error");
      })
      .finally(() => {
        settledPromiseCount++;
      });
  }
}

const totalCount = ref(0);
let finishedCount = ref(0);
let errorCount = ref(0);
let students = [];

const config = store.pageInputs["/image-download"];
// console.log("config::", config);
onMounted(async () => {
  const storePassedToNodeJs = JSON.parse(JSON.stringify(store));
  const color = storePassedToNodeJs.config.watermark.color;
  const nextColor = storePassedToNodeJs.config.watermark.nextColor;
  // const otherColor = storePassedToNodeJs.config.watermark.otherColor;
  const otherColor = "#ddd";
  try {
    if (config.type === "1") {
      console.log("download start ", Date.now());
      const res = await countStudents(store.env.examId, {
        upload: true,
        withSheetUrl: true,
        withScoreDetail: config.watermark,
        withMarkTrack: config.watermark,
        withGroupScoreTrack: config.watermark && config.trackMode,
        examNumberIn: config.examNumber ?? "",
        subjectCodeIn: config.subjectCode ?? "",
      });
      totalCount.value = res.data;
      let totalImageDownloadTime = 0;
      for (
        let pageNumber = 0;
        pageNumber * 10 < totalCount.value;
        pageNumber++
      ) {
        const resStudents = await getStudents(
          store.env.examId,
          pageNumber + 1,
          10,
          {
            upload: true,
            withSheetUrl: true,
            withScoreDetail: config.watermark,
            withMarkTrack: config.watermark,
            withGroupScoreTrack: config.watermark && config.trackMode === "1",
            examNumberIn: config.examNumber ?? "",
            subjectCodeIn: config.subjectCode ?? "",
            sliceConfigFix: config.watermark && config.trackMode === "2",
          }
        );
        students = resStudents.data;

        const urls = students.reduce(
          (accumulator, stu) => accumulator.concat(stu.sheetUrls),
          []
        );
        // cacheImages(urls);

        for (const student of students) {
          let allTags = Object.values(student.tags || {})
            .filter((x) => !!x)
            .flat()
            .filter((v: any) => v.userId != 0);
          allTags.forEach((item: any) => {
            if (
              allTags.find((v: any) => {
                return (
                  v.groupNumber == item.groupNumber && v.userId != item.userId
                );
              })
            ) {
              item.hide = true;
            } else {
              item.hide = false;
            }
            if (
              allTags.find((v: any) => {
                return (
                  v.groupNumber == item.groupNumber &&
                  v.userRole &&
                  v.userRole !== "MARKER" &&
                  (item.userRole === "MARKER" || !item.userRole)
                );
              })
            ) {
              item.forceHide = true;
            }
          });

          // allTags.sort((a: any, b: any) => {
          //   return a.userId - b.userId;
          // });
          let colorMap: any = {};
          let headerColorMap: any = {};
          for (let i = 0; i < allTags.length; i++) {
            const tag: any = allTags[i];
            const { groupNumber } = tag;
            if (tag.userRole && tag.userRole !== "MARKER") {
              if (!headerColorMap[groupNumber + ""]) {
                headerColorMap[groupNumber + ""] = {};
                headerColorMap[groupNumber + ""][tag.userId + ""] = "#008000";
                continue;
              } else {
                headerColorMap[groupNumber + ""][tag.userId + ""] = "#008000";
                continue;
              }
            }
            if (!colorMap[groupNumber + ""]) {
              colorMap[groupNumber + ""] = {};
              colorMap[groupNumber + ""][tag.userId + ""] = color;
            } else {
              let targetGroupObjKeys = Object.keys(colorMap[groupNumber + ""]);
              const len = targetGroupObjKeys.length;
              if (len == 1) {
                if (!targetGroupObjKeys.includes(tag.userId + "")) {
                  colorMap[groupNumber + ""][tag.userId + ""] = nextColor;
                } else {
                  continue;
                }
              } else if (len > 1) {
                if (!targetGroupObjKeys.includes(tag.userId + "")) {
                  colorMap[groupNumber + ""][tag.userId + ""] = otherColor;
                } else {
                  continue;
                }
              }
              // let c = len === 1 ? nextColor : otherColor;
              // colorMap[groupNumber + ""][tag.userId + ""] = c;
            }
          }
          console.log("allTags:", allTags);
          // console.log("colorMap:", colorMap);
          let resultImgList: any[] = [];
          let sheetUrlsLength = (student.sheetUrls || []).length;
          for (const sheetUrl of student.sheetUrls) {
            if (stopSignal) return;
            try {
              const index = student.sheetUrls.indexOf(sheetUrl);
              student.index = index + 1;
              student.examId = store.env.examId;
              // const filePath = window.electron.join(
              //   config.dir,
              //   mustache.render(config.template, student)
              // );
              const filePath = [
                config.dir,
                mustache.render(config.template, student),
              ];
              if (config.append && window.electron.existsImage(filePath)) {
                console.log(filePath + " already exists");
                // 执行到这里时，可能图片已经cache了
                urls.splice(urls.indexOf(sheetUrl), 1);
                // console.log(urls);
                continue;
              }
              console.debug("start ", sheetUrl);
              const imageDownloadStartTime = Date.now();
              const imageRes = await httpApp.get(sheetUrl, {
                responseType: "blob",
                headers: {
                  "Cache-Control": "no-cache",
                },
              });
              totalImageDownloadTime += Date.now() - imageDownloadStartTime;

              const [width, height] = await getImageDim(imageRes.data);
              const arrayBuffer = await imageRes.data.arrayBuffer();

              // console.log(imageRes.data);
              // console.log(await imageRes.data.arrayBuffer());
              // console.log(new Uint8Array(await imageRes.data.arrayBuffer()));
              let onlyUsePdf = config.pdf == "2";
              console.log("colorMap:", colorMap);
              let fileLocation = await window.electron.addWatermark(
                storePassedToNodeJs,
                arrayBuffer,
                width,
                height,
                filePath,
                student,
                index + 1,
                config.trackMode,
                config.x,
                config.y,
                colorMap,
                headerColorMap,
                onlyUsePdf
              );
              resultImgList.push(fileLocation);
            } catch (error) {
              window.electron.errorLogger(student, sheetUrl, error);
              errorCount.value += 1;
              if (config.failover) {
                throw error;
              } else {
                console.log(student, error);
                continue;
              }
            }
          }
          // 下载完一个学生
          finishedCount.value += 1;
          if (
            resultImgList.length === sheetUrlsLength &&
            (config.pdf == "2" || config.pdf == "3")
          ) {
            let imgName = "";
            if (config.template.lastIndexOf("/") > -1) {
              imgName = config.template.slice(
                config.template.lastIndexOf("/") + 1
              );
            } else {
              imgName = config.template;
            }
            let n = imgName.slice(0, imgName.lastIndexOf("."));
            let pdfName = n
              .match(/{{.*?}}/g)
              ?.map((item: any) => {
                return item.replace("{{", "").replace("}}", "");
              })
              .filter((v) => !!v)
              .map((item: any) => {
                return item === "index" ? "" : student[item];
              })
              .filter((v) => !!v)
              .join("-");

            window.electron.saveToPDF(resultImgList, [
              config.dir,
              `/pdfs/${store.env.examId}/${student.subjectCode}/${pdfName}.pdf`,
            ]);
          }
        }
      }
      console.log(
        "all end at ",
        Date.now(),
        " totalImageDownloadTime: ",
        totalImageDownloadTime
      );
    } else if (config.type === "2") {
      await processPackage();
    }

    const modal = Modal.success({});
    modal.update({
      title: "图片下载完成",
      content: "完成",
      onOk: () => router.back(),
    });
  } catch (error) {
    const modal = Modal.error({});
    console.log(error);
    modal.update({
      title: "图片下载出错",
      content: error.message || error,
      onOk: () => router.back(),
    });
  }
});

async function processPackage() {
  const res = await getPackages(store.env.examId, true, true);
  const array = res.data;
  totalCount.value = array.length;
  const urls = array.reduce((accumulator, p) => accumulator.concat(p.urls), []);
  // cacheImages(urls);

  for (let i = 0; i < array.length; i++) {
    const p = array[i];
    p.examId = store.env.examId;
    for (let i = 0; i < p.urls.length; i++) {
      if (stopSignal) return;
      try {
        const index = i + 1;
        p.index = index;
        const filePath = [config.dir, mustache.render(config.template, p)];
        if (config.append && window.electron.existsImage(filePath)) {
          console.log(filePath + " already exists");
          urls.splice(urls.indexOf(p.urls[i]), 1);
          continue;
        }

        const imageRes = await httpApp.get(p.urls[i], {
          responseType: "blob",
        });

        await window.electron.saveImage(
          JSON.parse(JSON.stringify(store)),
          await imageRes.data.arrayBuffer(),
          filePath
        );
      } catch (error) {
        window.electron.errorLogger2(p.urls[i], error);
        errorCount.value += 1;
        if (config.failover) {
          throw error;
        } else {
          console.log(p, error);
          continue;
        }
      }
    }
    finishedCount.value += 1;
  }
}

let stopSignal = false;
onUnmounted(() => (stopSignal = true));
</script>
