import {
  getSingleStudentTaskOfStudentTrack,
  studentObjectiveConfirmData,
  getSingleStudentCardData,
} from '@/api/task';
import { Task, Track, SpecialTag, Question } from '@/api/types/task';
import { TrackConfigType } from '@/store/modules/app/types';
import { PictureTypeEnum, PICTURE_TYPE } from '@/constants/enumerate';
import { calcSum, deepCopy, maxNum, strGbLen } from '@/utils/utils';

import { DrawTrackItem } from '../../../../electron/preload/types';
import { TrackTaskData } from '../../../../electron/db/models/trackTask';
import { TrackTaskDetailData } from '../../../../electron/db/models/trackTaskDetail';

type AnswerMap = Record<
  string,
  { answer: string; isRight: boolean; score: number; totalScore: number }
>;

interface TrackItemType {
  url: string;
  width: number;
  height: number;
  outpath: string;
  drawTrackList: DrawTrackItem[];
}

type ElementType =
  | 'FILL_QUESTION'
  | 'FILL_LINE'
  | 'EXPLAIN'
  | 'COMPOSITION'
  | 'TOPIC_HEAD'
  | 'CARD_HEAD';
interface CardBaseElement {
  id: string;
  type: ElementType;
  topicNo: number;
  startNumber: number;
  questionsCount: number;
}
interface CardElement extends CardBaseElement {
  parent: CardBaseElement;
}
interface CardDataItem {
  exchange: {
    answer_area: Array<{
      main_number: number;
      sub_number: number | string;
      area: [number, number, number, number];
    }>;
    fill_area: Array<{
      field: 'question' | 'examNumber';
      index: number;
      single: boolean;
      horizontal: boolean;
      items: Array<{
        main_number: number;
        sub_number: number | string;
        options: [number, number, number, number][];
      }>;
    }>;
  };
  columns: Array<{
    elements: CardElement[];
  }>;
}

interface CardContentType {
  pages: CardDataItem[];
}

interface QuestionItem {
  mainNumber: number;
  subNumber: number | string;
}
interface QuestionArea {
  i: number;
  x: number;
  y: number;
  w: number;
  h: number;
  qStruct: string;
}

type UserMapType = Record<
  string,
  {
    userId: string;
    userName: string;
    color: string;
    scores: Array<{ subNumber: string; score: number }>;
  }
>;

interface ImageItem {
  url: string;
  width: number;
  height: number;
}

interface PaperRecogData {
  page_index: number;
  question: Array<{
    index: number;
    fill_result: Array<{
      main_number: number;
      sub_number: number;
      single: number;
      fill_option: number[];
      suspect_flag: number;
      fill_position: string[];
      fill_size: number[];
    }>;
  }>;
}

interface DrawConfig {
  domain: string;
  task: TrackTaskData;
  taskDetail: TrackTaskDetailData;
  trackConfig: TrackConfigType;
  winId: number;
}

export default function useDraw(drawConfig: DrawConfig) {
  const { domain, task, taskDetail, trackConfig, winId } = drawConfig;

  let answerMap = {} as AnswerMap;
  let cardData = [] as CardDataItem[];
  let recogDatas: string[] = [];
  let rawTask = {} as Task;
  let trackData = [] as TrackItemType[];
  let originImgs = [] as ImageItem[];
  let trackFiles = [] as ImageItem[];
  let curStudentId = '';
  const hasOrigin = trackConfig.pictureType.includes('origin');
  const hasTrack = trackConfig.pictureType.includes('track');
  const hasPdf = trackConfig.pictureType.includes('pdf');

  const defaultColorConfig = {
    track: ['red', 'blue', 'gray'],
    head: 'green',
  };
  let colorConfig = { track: ['red', 'blue', 'gray'], head: 'green' };

  function updateColorConfig() {
    if (trackConfig.trackColorType === 'ALL_RED') {
      colorConfig.head = 'red';
      colorConfig.track = ['red', 'red', 'red'];
    } else {
      colorConfig = deepCopy(defaultColorConfig);
    }
  }
  updateColorConfig();

  function addLog(content: string, type?: 'info' | 'error') {
    window.api.logger(`win:${winId} ${content}`, type);
  }

  async function runTask() {
    initData();
    curStudentId = taskDetail.studentId;
    addLog(`[${curStudentId}] 01-开始任务`);

    let result = true;
    try {
      await getTaskData(curStudentId);
      addLog(`[${curStudentId}] 02-获取任务数据成功`);

      originImgs = await downloadImages(rawTask.sheetUrls);
      addLog(`[${curStudentId}] 02-1-图片下载成功`);

      if (hasTrack || hasPdf) {
        await parseDrawList();
        addLog(`[${curStudentId}] 03-解析绘制数据成功`);

        trackFiles = await drawTask();
        addLog(`[${curStudentId}] 04-绘制成功`);

        if (hasPdf) {
          await window.api.imagesToPdf(trackFiles, getOutputPath('pdf'));
          addLog(`[${curStudentId}] 05-生成pdf成功`);
        }
      }

      clearResult();
    } catch (error) {
      const e = error as Error;
      console.log(e);
      addLog(
        `[${curStudentId}-${rawTask.studentCode}] 08-任务失败，原因：${
          e.message || '未知'
        }`,
        'error'
      );
      result = false;
    }

    const status = result ? 'FINISH' : 'INIT';
    await window.db.updateTrackTaskDetailStatus({
      id: taskDetail.id,
      status,
    });

    addLog(`[${curStudentId}] 09-任务结束`);
    return true;
  }

  function initData() {
    cardData = [] as CardDataItem[];
    recogDatas = [] as string[];
    rawTask = {} as Task;
    trackData = [] as TrackItemType[];
    answerMap = {} as AnswerMap;
    originImgs = [] as ImageItem[];
    trackFiles = [] as ImageItem[];
    curStudentId = '';
  }

  function clearResult() {
    if (!hasOrigin) {
      window.api.clearFilesSync(originImgs.map((item) => item.url));
    }
    if (!hasTrack && trackFiles.length) {
      window.api.clearFilesSync(trackFiles.map((item) => item.url));
    }
  }

  async function getTaskData(studentId: string) {
    const funcs = [
      async () => {
        rawTask = await getSingleStudentTaskOfStudentTrack(studentId);
        if (!rawTask.sheetUrls) rawTask.sheetUrls = [];
      },
    ];

    if (hasTrack || hasPdf) {
      funcs.push(async () => {
        // 获取客观题选项信息
        const objectiveData = await studentObjectiveConfirmData(studentId);
        objectiveData.answers.forEach((item) => {
          answerMap[`${item.mainNumber}_${item.subNumber}`] = {
            answer: item.answer,
            totalScore: item.totalScore,
            score: item.score,
            isRight: item.answer === item.standardAnswer,
          };
        });
        recogDatas = objectiveData.sheetUrls.map((item) => item.recogData);
      });

      funcs.push(async () => {
        // 获取题卡数据
        const cardRes = await getSingleStudentCardData(studentId);
        const cardContent =
          cardRes && cardRes.content
            ? (JSON.parse(cardRes.content) as CardContentType)
            : { pages: [] };
        cardData = cardContent.pages;
      });
    }

    await Promise.all(funcs.map((func) => func()));
  }

  /**
   * 获取文件存储路径，规则：学期/考试/课程/试卷编号/教学班/下载文件类型/学生图片
   */
  function getOutputPath(type: PictureTypeEnum, index?: number) {
    const transfromStr = (str: string) => str.replace(/[*|:?<>]/g, '');
    let filename =
      trackConfig.studentFileRule === 'CODE_NAME'
        ? `${rawTask.studentCode}-${rawTask.studentName}`
        : rawTask.studentCode;
    filename = transfromStr(filename);

    if (index !== undefined) {
      filename += `-${index}`;
    }
    filename += type === 'pdf' ? '.pdf' : '.jpg';

    const paths = [
      trackConfig.curOutputDir,
      transfromStr(task.semesterName),
      transfromStr(task.examName),
      transfromStr(`${rawTask.courseName}(${rawTask.courseCode})`),
      transfromStr(rawTask.paperNumber),
      transfromStr(taskDetail.className),
    ];
    if (trackConfig.pictureType.includes(type)) {
      paths.push(PICTURE_TYPE[type]);
    } else {
      filename = `${type}-${filename}`;
    }
    paths.push(filename);

    return window.api.joinPath(paths);
  }

  async function downloadImages(urls: string[]) {
    const downloads: Promise<ImageItem>[] = [];
    for (let i = 0; i < urls.length; i++) {
      let url = urls[i];
      if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = `${domain}/${url}`;
      }
      // url += `&t=${Date.now()}`;
      downloads.push(
        window.api.downloadImage(url, getOutputPath('origin', i + 1))
      );
    }

    const images = await Promise.all(downloads).catch((error) => {
      console.log(error);
    });
    if (!images) {
      return Promise.reject(new Error('下载图片失败'));
    }
    return images;
  }

  async function parseDrawList() {
    trackData = [];

    const trackLists = (rawTask.questionList || [])
      .map((q) => {
        return q.headerTrack?.length
          ? addHeaderTrackColorAttr(q.headerTrack)
          : addTrackColorAttr(q.trackList);
      })
      .flat();

    const markDeailList = parseMarkDetailList(originImgs);
    const objectiveAnswerTagList = parseObjectiveAnswerTags(originImgs);

    for (let i = 0; i < originImgs.length; i++) {
      const img = originImgs[i];
      const drawTrackList = [] as DrawTrackItem[];
      trackLists
        .filter((item) => item.offsetIndex === i + 1)
        .forEach((item) => {
          drawTrackList.push(getDrawTrackItem(item));
        });
      (rawTask.specialTagList || [])
        .filter((item) => item.offsetIndex === i + 1)
        .forEach((item) => {
          drawTrackList.push(getDrawTagTrackItem(item));
        });
      const answerTags = paserRecogData(i);
      drawTrackList.push(...answerTags);
      drawTrackList.push(...(markDeailList[i] || []));
      const oTags = (objectiveAnswerTagList[i] || []).map(
        (tag) => tag.trackItem
      );
      drawTrackList.push(...oTags);
      drawTrackList.push(getTotalTrack(img));

      trackData[i] = {
        url: img.url,
        width: img.width,
        height: img.height,
        outpath: getOutputPath('track', i + 1),
        drawTrackList,
      };
    }

    if (!cardData.length) {
      const summarys = parseMode4Data(originImgs[0]);
      trackData[0].drawTrackList.push(...summarys);
    }
  }

  async function drawTask(): Promise<ImageItem[]> {
    if (!trackData.length) return [];

    await window.api.drawTracks(trackData);

    return trackData.map((item) => {
      return {
        url: item.outpath,
        width: item.width,
        height: item.height,
      };
    });
  }

  // track ----- start->
  const trackTextFontSize = 30;
  const trackInfoFontSize = 20;
  const trackInfoLineHeight = 20 * 1.2;
  function getDrawTrackItem(track: Track): DrawTrackItem {
    return {
      type: 'text',
      option: {
        x: track.offsetX,
        y: track.offsetY - trackTextFontSize / 2,
        text: String(track.score),
        color: track.color,
        fontSize: trackTextFontSize,
      },
    };
  }

  function getDrawTagTrackItem(track: SpecialTag): DrawTrackItem {
    if (track.tagType === 'LINE') {
      const tagProp = JSON.parse(track.tagName) as {
        len: number;
      };

      return {
        type: 'line',
        option: {
          x0: track.offsetX,
          y0: track.offsetY,
          x1: track.offsetX + tagProp.len,
          y1: track.offsetY,
        },
      };
    }

    if (track.tagType === 'CIRCLE') {
      const tagProp = JSON.parse(track.tagName) as {
        width: number;
        height: number;
      };

      return {
        type: 'circle',
        option: {
          x0: track.offsetX,
          y0: track.offsetY,
          x1: track.offsetX + tagProp.width,
          y1: track.offsetY + tagProp.height,
        },
      };
    }

    return {
      type: 'text',
      option: {
        x: track.offsetX,
        y: track.offsetY - trackTextFontSize / 2,
        text: track.tagName,
        color: track.color,
        fontSize: trackTextFontSize,
      },
    };
  }

  function addHeaderTrackColorAttr(headerTrack: Track[]): Track[] {
    return headerTrack.map((item) => {
      item.color = colorConfig.head;
      return item;
    });
  }

  function addTrackColorAttr(tList: Track[]): Track[] {
    let markerIds: string[] = tList.map((v) => v.userId).filter((x) => !!x);
    markerIds = Array.from(new Set(markerIds));
    // markerIds.sort();
    const colorMap: Record<string, string> = {};
    for (let i = 0; i < markerIds.length; i++) {
      const mId = markerIds[i];
      if (i === 0) {
        colorMap[mId] = colorConfig.track[0];
      } else if (i === 1) {
        colorMap[mId] = colorConfig.track[1];
      } else if (i > 1) {
        colorMap[mId] = colorConfig.track[2];
      }
    }
    type ColorK = keyof typeof colorMap;
    tList = tList.map((item: Track) => {
      const k = item.userId as ColorK;
      item.color = colorMap[k] || 'red';
      item.isByMultMark = markerIds.length > 1;
      return item;
    });
    return tList;
  }

  /* 
  function addTagColorAttr(tList: SpecialTag[]): SpecialTag[] {
    let markerIds: string[] = tList
      .map((v) => String(v.userId))
      .filter((x) => !!x);
    markerIds = Array.from(new Set(markerIds));
    // markerIds.sort();
    const colorMap: Record<string, string> = {};
    for (let i = 0; i < markerIds.length; i++) {
      const mId = markerIds[i];
      if (i === 0) {
        colorMap[mId] = 'red';
      } else if (i === 1) {
        colorMap[mId] = 'blue';
      } else if (i > 1) {
        colorMap[mId] = 'gray';
      }
    }
    type ColorK = keyof typeof colorMap;
    tList = tList.map((item: SpecialTag) => {
      const k = String(item.userId) as ColorK;
      item.color = colorMap[k] || 'red';
      item.isByMultMark = markerIds.length > 1;
      return item;
    });
    return tList;
  }
  */

  // track ----- end->

  // mark detail ----- start->
  // 解析各试题答题区域以及评分
  function parseMarkDetailList(images: ImageItem[]): Array<DrawTrackItem[]> {
    const dataList: Array<DrawTrackItem[]> = [];
    const questions = rawTask.questionList || [];

    const fillQues = getFillLines();
    let fillQuestions = [] as Question[];
    let otherQuestions = questions;
    if (Object.keys(fillQues).length) {
      const fillQNos = Object.values(fillQues).flat();
      fillQuestions = questions.filter((q) =>
        fillQNos.includes(`${q.mainNumber}_${q.subNumber}`)
      );
      otherQuestions = questions.filter(
        (q) => !fillQNos.includes(`${q.mainNumber}_${q.subNumber}`)
      );
    }
    // 填空题：合并所有小题为一个区域
    Object.values(fillQues).forEach((qnos) => {
      const groupQuestions = fillQuestions.filter((q) =>
        qnos.includes(`${q.mainNumber}_${q.subNumber}`)
      );
      const areas = parseQuestionAreas(groupQuestions);
      if (!areas.length) return;
      const area = { ...areas[0] };
      const imgIndex = area.i - 1;
      if (!dataList[imgIndex]) {
        dataList[imgIndex] = [];
      }

      const img = images[imgIndex] as ImageItem;
      area.x *= img.width;
      area.y *= img.height;
      area.w *= img.width;

      const dataArr = dataList[imgIndex];
      const userMap: UserMapType = {};
      const isDoubleMark = !groupQuestions.some((question) => {
        let userIds = question.trackList.map((track) => track.userId);
        if (
          !userIds.length &&
          question.markerList &&
          question.markerList.length
        ) {
          userIds = question.markerList
            .filter((marker) => !marker.header)
            .map((marker) => marker.userId);
        }
        const uids = new Set(userIds);
        return uids.size === 1;
      });
      groupQuestions.forEach((question) => {
        question.trackList.forEach((track) => {
          if (!userMap[track.userId]) {
            userMap[track.userId] = {
              userId: track.userId,
              userName: track.userName,
              color: track.color || '',
              scores: [],
            };
          }
          const existUserScore = userMap[track.userId].scores.find(
            (s) => s.subNumber === track.subNumber
          );
          if (existUserScore) {
            existUserScore.score += track.score;
          } else {
            userMap[track.userId].scores.push({
              score: track.score,
              subNumber: track.subNumber,
            });
          }
        });

        // 普通模式没有轨迹
        if (
          !question.trackList.length &&
          question.markerList &&
          question.markerList.length
        ) {
          question.markerList
            .filter((marker) => !marker.header)
            .forEach((marker) => {
              if (!userMap[marker.userId]) {
                userMap[marker.userId] = {
                  userId: marker.userId,
                  userName: marker.userName,
                  color: marker.header ? 'green' : 'red',
                  scores: [],
                };
              }
              userMap[marker.userId].scores.push({
                score: marker.score,
                subNumber: question.subNumber,
              });
            });
        }
      });

      // 填空题的打分需要自动换行，目前一行只展示最多10个评分
      let offsetY = -1 * trackInfoLineHeight;
      Object.values(userMap).forEach((user, index) => {
        const zhs = ['一', '二', '三'];
        const prename = isDoubleMark ? `${zhs[index] || ''}评` : '评卷员';

        const userScore = user.scores.map(
          (item) => `${item.subNumber}:${item.score}分`
        );
        const lineScoreCount = 10;
        const groupCount = Math.ceil(userScore.length / lineScoreCount);
        const groups: string[] = [];
        for (let i = 0; i < groupCount; i++) {
          groups.push(
            userScore
              .slice(i * lineScoreCount, (i + 1) * lineScoreCount)
              .join('，')
          );
        }

        offsetY += trackInfoLineHeight;
        dataArr.push({
          type: 'text',
          option: {
            x: area.x,
            y: area.y + offsetY,
            text: `${prename}：${user.userName}，评分：`,
            fontSize: trackInfoFontSize,
            color: user.color,
          },
        });
        groups.forEach((group) => {
          offsetY += 20;
          dataArr.push({
            type: 'text',
            option: {
              x: area.x,
              y: area.y + offsetY,
              text: group,
              fontSize: trackInfoFontSize,
              color: user.color,
            },
          });
        });
      });

      const score = calcSum(groupQuestions.map((item) => item.score || 0));
      const maxScore = calcSum(groupQuestions.map((item) => item.maxScore));
      const tCont = `得分：${score}，满分：${maxScore}`;
      const tContLen = strGbLen(tCont) / 2;
      dataArr.push({
        type: 'text',
        option: {
          x: area.x + area.w - Math.ceil(tContLen * trackTextFontSize),
          y: area.y,
          text: tCont,
          fontSize: trackTextFontSize,
        },
      });
    });

    // 其他试题
    otherQuestions.forEach((question) => {
      const areas = parseQuestionAreas([question]);
      if (!areas.length) return;
      const area = { ...areas[0] };
      const imgIndex = area.i - 1;
      if (!dataList[imgIndex]) {
        dataList[imgIndex] = [];
      }

      const img = images[imgIndex] as ImageItem;
      area.x *= img.width;
      area.y *= img.height;
      area.w *= img.width;

      const dataArr = dataList[imgIndex];
      const userMap: UserMapType = {};
      const isArbitration = Boolean(question.headerTrack?.length);
      const tList = isArbitration
        ? (question.headerTrack as Track[])
        : question.trackList;
      tList.forEach((track) => {
        if (!userMap[track.userId]) {
          userMap[track.userId] = {
            userId: track.userId,
            userName: track.userName,
            color: track.color || '',
            scores: [],
          };
        }
        userMap[track.userId].scores.push({
          score: track.score,
          subNumber: track.subNumber,
        });
      });

      // 普通模式没有轨迹
      if (!tList.length && question.markerList && question.markerList.length) {
        let markers = question.markerList.filter((marker) => marker.header);
        if (!markers.length) {
          markers = question.markerList.filter((marker) => !marker.header);
        }
        markers.forEach((marker) => {
          userMap[marker.userId] = {
            userId: marker.userId,
            userName: marker.userName,
            color: marker.header ? 'green' : 'red',
            scores: [{ score: marker.score, subNumber: question.subNumber }],
          };
        });
      }

      const isDoubleMark = Object.values(userMap).length > 1;
      Object.values(userMap).forEach((user, index) => {
        const zhs = ['一', '二', '三'];
        let prename = '';
        if (isArbitration) {
          prename = '仲裁';
        } else {
          prename = isDoubleMark ? `${zhs[index] || ''}评` : '评卷员';
        }

        const userScore = calcSum(user.scores.map((item) => item.score));
        const content = `${prename}：${user.userName}，评分：${userScore}`;
        dataArr.push({
          type: 'text',
          option: {
            x: area.x,
            y: area.y + index * trackInfoLineHeight,
            text: content,
            fontSize: trackInfoFontSize,
            color: user.color,
          },
        });
      });
      const tCont = `得分：${question.score}，满分：${question.maxScore}`;
      const tContLen = strGbLen(tCont) / 2;
      dataArr.push({
        type: 'text',
        option: {
          x: area.x + area.w - Math.ceil(tContLen * trackTextFontSize),
          y: area.y,
          text: tCont,
          fontSize: trackTextFontSize,
        },
      });
    });

    return dataList;
  }

  function getTotalTrack(image: ImageItem): DrawTrackItem {
    const totalScore = rawTask.markerScore || 0;
    const objectiveScore = rawTask.objectiveScore || 0;
    const subjectiveScore = totalScore - objectiveScore;
    return {
      type: 'text',
      option: {
        x: 0.15 * image.width,
        y: 0.01 * image.height,
        text: `总分：${totalScore}，主观题得分：${subjectiveScore}，客观题得分：${objectiveScore}`,
        fontSize: 40,
      },
    };
  }

  // 获取属于填空题的试题号
  function getFillLines() {
    const questions: Record<number, string[]> = {};
    cardData.forEach((page) => {
      page.columns.forEach((column) => {
        column.elements.forEach((element) => {
          if (element.type !== 'FILL_LINE') return;

          if (!questions[element.topicNo]) questions[element.topicNo] = [];

          for (let i = 0; i < element.questionsCount; i++) {
            questions[element.topicNo].push(
              `${element.topicNo}_${element.startNumber + i}`
            );
          }
        });
      });
    });
    return questions;
  }

  function parseQuestionAreas(questions: QuestionItem[]) {
    if (!questions.length || !cardData?.length) return [];

    const pictureConfigs: QuestionArea[] = [];
    const structs = questions.map(
      (item) => `${item.mainNumber}_${item.subNumber}`
    );
    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;
    });
    const combinePictureConfigList: QuestionArea[] = [];
    let prevConfig = {} as QuestionArea;
    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;
  }
  // mark detail ----- end->

  // answer tag ----- start->
  // 解析客观题答案展示位置
  function paserRecogData(imageIndex: number): DrawTrackItem[] {
    if (!recogDatas.length || !recogDatas[imageIndex]) return [];

    const recogData: PaperRecogData = JSON.parse(
      window.atob(recogDatas[imageIndex])
    );
    const answerTags: DrawTrackItem[] = [];
    recogData.question.forEach((question) => {
      question.fill_result.forEach((result) => {
        const fillPositions = result.fill_position.map((pos) => {
          return pos.split(',').map((n) => Number(n));
        });

        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];

        const { answer, isRight } =
          answerMap[`${result.main_number}_${result.sub_number}`] || {};

        answerTags.push({
          type: 'text',
          option: {
            x: tagLeft,
            y: tagTop,
            text: answer || '',
            color: isRight ? '#05b575' : '#f53f3f',
          },
        });
      });
    });

    return answerTags;
  }
  // answer tag ----- end->

  // objective answer tag ----- start->
  interface ObjectiveAnswerTagItem {
    id: string;
    mainNumber: number;
    subNumbers: string;
    score: number;
    totalScore: number;
    trackItem: DrawTrackItem;
  }
  function parseObjectiveAnswerTags(images: ImageItem[]) {
    const objectiveAnswerTags: Array<ObjectiveAnswerTagItem[]> = [];

    if (!cardData?.length) return objectiveAnswerTags;

    cardData.forEach((page, pindex) => {
      if (!objectiveAnswerTags[pindex]) objectiveAnswerTags[pindex] = [];
      const img = images[pindex] as ImageItem;

      page.columns.forEach((column) => {
        column.elements.forEach((element) => {
          if (element.type !== 'FILL_QUESTION') return;

          const ogroup = objectiveAnswerTags.find((tgroup) =>
            tgroup.some((oitem) => oitem.id === element.parent.id)
          );
          if (ogroup) return;

          const parent = element.parent;
          const oaTagItem: ObjectiveAnswerTagItem = {
            id: parent.id,
            mainNumber: parent.topicNo,
            subNumbers: `${parent.startNumber}~${
              parent.startNumber + parent.questionsCount - 1
            }`,
            score: 0,
            totalScore: 0,
            trackItem: {} as DrawTrackItem,
          };
          // 位置
          const area = { x: 0, y: 0, w: 0.44 };
          page.exchange.fill_area.forEach((fa) => {
            fa.items.forEach((fitem) => {
              if (
                fitem.main_number === oaTagItem.mainNumber &&
                fitem.sub_number === parent.startNumber
              ) {
                const [x, y] = fitem.options[0];
                area.x = x;
                area.y = y;
              }
            });
          });
          area.x = (area.x - 0.015) * img.width;
          area.y = (area.y - 0.04) * img.height;
          area.w *= img.width;

          // 分数统计
          const questions: Array<{ score: number; totalScore: number }> = [];
          for (let i = 0; i < parent.questionsCount; i++) {
            const qans = answerMap[
              `${parent.topicNo}_${i + parent.startNumber}`
            ] || { score: 0, totalScore: 0 };
            questions[i] = {
              score: qans.score,
              totalScore: qans.totalScore,
            };
          }
          oaTagItem.score = calcSum(questions.map((q) => q.score || 0));
          oaTagItem.totalScore = calcSum(
            questions.map((q) => q.totalScore || 0)
          );
          const tCont = `得分：${oaTagItem.score}，满分：${oaTagItem.totalScore}`;
          const tContLen = strGbLen(tCont) / 2;
          oaTagItem.trackItem = {
            type: 'text',
            option: {
              x: area.x + area.w - Math.ceil(tContLen * trackTextFontSize),
              y: area.y,
              text: tCont,
              fontSize: trackTextFontSize,
            },
          };

          objectiveAnswerTags[pindex].push(oaTagItem);
        });
      });
    });

    return objectiveAnswerTags;
  }
  // objective answer tag ----- end->

  // mode4 data
  function getMode4MarkerName(q: Question): string {
    let markerName = '';
    if (q.headerTrack && q.headerTrack.length) {
      markerName = q.headerTrack[0].userName;
    } else if (q.trackList && q.trackList.length) {
      markerName = q.trackList[0].userName;
    } else if (q.markerList && q.markerList.length) {
      let markers = q.markerList.filter((marker) => marker.header);
      if (!markers.length) {
        markers = q.markerList.filter((marker) => !marker.header);
      }
      if (markers.length) markerName = markers[0].userName;
    }
    return markerName;
  }

  function parseMode4Data(img: ImageItem): DrawTrackItem[] {
    const isDoubleMark = (rawTask.questionList || []).some((question) => {
      let userIds = question.trackList.map((track) => track.userId);
      if (
        !userIds.length &&
        question.markerList &&
        question.markerList.length
      ) {
        userIds = question.markerList
          .filter((marker) => !marker.header)
          .map((marker) => marker.userId);
      }
      const uids = new Set(userIds);
      return uids.size === 2;
    });
    if (isDoubleMark) return [];

    const dataList: DrawTrackItem[] = [];
    const sources: string[][] = [['主观题号', '分数', '评卷员']];

    (rawTask.questionList || []).forEach((q) => {
      sources.push([
        `${q.mainNumber}-${q.subNumber}`,
        `${q.score}`,
        getMode4MarkerName(q),
      ]);
    });

    const rowX = img.width * 0.05;
    const rowY = img.height * 0.11;
    // const rowW = img.width * 0.45;
    const columnOffsetLeft = [0, 150, 80 + 150];

    sources.forEach((source, sindex) => {
      source.forEach((cont, cindex) => {
        dataList.push({
          type: 'text',
          option: {
            x: rowX + columnOffsetLeft[cindex],
            y: rowY + sindex * trackInfoLineHeight,
            text: cont,
            color: 'red',
            fontSize: trackInfoFontSize,
          },
        });
      });
    });

    return dataList;
  }

  return {
    runTask,
  };
}
