<template>
  <div class="paper-template-build">
    <div class="paper-template-build-body">
      <div class="margin_top_10">
        <el-select
          v-model="seqMode"
          class="margin-right-10"
          @change="seqModeChange"
        >
          <el-option value="MODE1" label="单题型连续"></el-option>
          <el-option value="MODE2" label="客观题整体连续"></el-option>
          <el-option value="MODE3" label="按大题独立"></el-option>
          <el-option value="MODE5" label="整卷连续"></el-option>
        </el-select>
        <el-select
          v-model="curPaperTemp"
          class="margin-right-10"
          placeholder="请选择"
          value-key="id"
          @change="paperTempChange"
        >
          <el-option
            v-for="item in paperTempList"
            :key="item.id"
            :label="item.name"
            :value="item"
          >
          </el-option>
        </el-select>
        <el-button type="primary" @click="toDownload">下载试卷</el-button>
      </div>
      <paper-template-view
        ref="PaperTemplateView"
        class="preview-body"
        :pages="pages"
        :page-config="paperTempJson.pageConfig"
      ></paper-template-view>
    </div>
  </div>
</template>

<script>
import PaperTemplateView from "../components/PaperTemplateView.vue";
import { getModel as getRichTextModel } from "../elements/rich-text/model";
import { getModel as getPageModel } from "../elements/page/model";
import { getElementId, randomCode, deepCopy } from "../../card/plugins/utils";
import { calcSum, maxNum } from "@/plugins/utils";
import previewTemp from "../previewTemp";
import { paperDetailInfoApi } from "../../paper/api";
import { paperTemplateListApi, paperPdfDownloadApi } from "../api";
import { downloadByApi } from "@/plugins/download";
// import paperJson from "./data/paper.json";
// import paperTempJson from "./data/paper-temp.json";

const numberToUpperCase = function (val) {
  if (val < 1 || val > 26) return;

  return String.fromCharCode(64 + val);
};

const checkRichTextHasCont = function (data) {
  if (!data) return false;
  if (!data.sections || !data.sections.length) return false;

  if (!data.sections[0].blocks || !data.sections[0].blocks.length) return false;

  return true;
};

export default {
  name: "PaperTemplateBuild",
  components: { PaperTemplateView },
  data() {
    return {
      paperId: this.$route.params.paperId,
      viewType: this.$route.params.viewType,
      seqMode: "MODE1",
      renderStructList: [],
      pages: [],
      paperJson: {},
      paperTempJson: {
        pages: [],
        pageConfig: {},
      },
      maxColumnWidth: 200,
      maxColumnHeight: 200,
      paperTempList: [],
      curPaperTemp: {},
      downloading: false,
      fieldData: {},
      paperStructs: [],
    };
  },
  mounted() {
    if (this.viewType === "frame") {
      this.initFrame();
      return;
    }
    this.initData();
  },
  methods: {
    async initFrame() {
      try {
        const paperSet = window.parent.paperSet;
        if (!paperSet) {
          this.emitFrameResult(false, "数据缺失");
          return;
        }

        this.seqMode = paperSet.seqMode;
        this.curPaperTemp = paperSet.paperTemp;

        await this.getPaperJson();

        let paperTempJson = this.curPaperTemp.content
          ? JSON.parse(this.curPaperTemp.content)
          : { pages: [], pageConfig: {} };
        this.paperTempJson = paperTempJson;
        this.pages = paperTempJson.pages;
        this.updaterFieldInfo();
      } catch (error) {
        this.emitFrameResult(false, "数据错误");
      }

      this.$nextTick(async () => {
        try {
          this.maxColumnWidth =
            document.getElementById("column-0-0").offsetWidth;
          this.maxColumnHeight =
            document.getElementById("column-0-0").offsetHeight;
          this.parseRenderStructList();
          this.buildPrePages();
        } catch (error) {
          this.emitFrameResult(false, "构建错误");
        }

        const loadRes = await this.waitAllImgLoaded().catch(() => {});
        if (!loadRes) {
          this.emitFrameResult(false, "数据缓存错误");
          return;
        }
        this.$nextTick(() => {
          try {
            this.buildReleasePages();
            this.emitFrameResult(true, "", this.getPreviewTemp());
          } catch (error) {
            this.emitFrameResult(false, "构建pdf错误");
          }
        });
      });
    },
    emitFrameResult(success = true, errorMsg = "", htmlCont = "") {
      window.parent &&
        window.parent.submitPaperTemp &&
        window.parent.submitPaperTemp({
          success,
          errorMsg,
          htmlCont,
          templateId: this.curPaperTemp.id,
        });
    },
    async initData() {
      await this.getPaperJson();
      await this.getPaperTempList();

      if (!this.paperTempList.length) {
        this.$message.error("导出模板缺失！");
        return;
      }
      this.paperTempChange(this.paperTempList[0]);

      // test--->
      // this.paperJson = paperJson;
      // this.paperTempJson = paperTempJson;
      // this.pages = paperTempJson.pages;
      // this.$nextTick(() => {
      //   this.buildData();
      // });
    },
    async getPaperJson() {
      const res = await paperDetailInfoApi({
        paperId: this.paperId,
        seqMode: this.seqMode,
      });
      this.paperJson = res.data;
      this.fieldData = {
        paperName: res.data.name,
        courseName: `${res.data.course.name}（${res.data.course.code}）`,
        totalScore: res.data.totalScore,
        rootOrgName: res.data.rootOrgName,
      };
      this.paperStructs = this.paperJson.paperDetails.map((detail) => {
        return {
          detailName: detail.name,
          questionCount: detail.unitCount,
          totalScore: detail.score,
        };
      });
    },
    async seqModeChange() {
      await this.getPaperJson();
      this.$nextTick(() => {
        this.buildData();
      });
    },
    async getPaperTempList() {
      const res = await paperTemplateListApi("PAPER_EXPORT");
      this.paperTempList = res.data;
    },
    paperTempChange(paperTemp) {
      // console.log(paperTemp);
      this.curPaperTemp = paperTemp;
      let paperTempJson = paperTemp.content
        ? JSON.parse(paperTemp.content)
        : { pages: [], pageConfig: {} };
      this.paperTempJson = paperTempJson;
      this.pages = paperTempJson.pages;
      this.updaterFieldInfo();

      this.$nextTick(() => {
        this.buildData();
      });
    },
    updaterFieldInfo() {
      const VALID_ELEMENTS_FOR_EXTERNAL = ["FIELD_TEXT"];
      this.paperTempJson.pages.forEach((page) => {
        page.columns.forEach((column) => {
          column.elements.forEach((elem) => {
            if (elem.type === "PAPER_STRUCT") {
              elem.structs = this.paperStructs;
            }

            if (!elem.elements || !elem.elements.length) return;

            elem.elements.forEach((element) => {
              if (!VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) return;

              if (element.type === "FIELD_TEXT" && element.field) {
                element.content = this.fieldData[element.field];
              }
            });
          });
        });
      });
    },
    async buildData() {
      this.maxColumnWidth = document.getElementById("column-0-0").offsetWidth;
      this.maxColumnHeight = document.getElementById("column-0-0").offsetHeight;
      this.parseRenderStructList();
      this.buildPrePages();

      const loadRes = await this.waitAllImgLoaded().catch(() => {});
      if (!loadRes) {
        this.$message.error("图片加载有误！");
        return;
      }
      this.$nextTick(() => {
        this.buildReleasePages();
      });
    },
    parseRenderStructList() {
      let renderStructList = [];

      this.paperJson.paperDetails.forEach((detail) => {
        renderStructList.push(this.parseDetailTitle(detail));
        if (checkRichTextHasCont(detail.description)) {
          const descData = this.parseTitleOption(detail.description, "");
          renderStructList.push(...descData);
        }
        detail.paperDetailUnits.forEach((question) => {
          let questionInfo = question.question;
          if (questionInfo.subQuestions && questionInfo.subQuestions.length) {
            const bodys = this.parseTitleOption(questionInfo.quesBody, "");
            renderStructList.push(...bodys);

            const isMatches = this.checkIsMatches(questionInfo.questionType);
            if (
              isMatches &&
              questionInfo.quesOptions &&
              questionInfo.quesOptions.length
            ) {
              questionInfo.quesOptions.forEach((op) => {
                const obodys = this.parseTitleOption(
                  op.optionBody,
                  `${numberToUpperCase(op.number)}、`,
                  "option"
                );
                renderStructList.push(...obodys);
              });
            }

            // 选词填空不展示小题
            if (questionInfo.questionType === "BANKED_CLOZE") {
              renderStructList.push(this.parseLineGap());
              return;
            }

            questionInfo.subQuestions.forEach((sq) => {
              if (isMatches) sq.quesOptions = []; // 选词填空、段落匹配小题中不展示选项
              const contents = this.parseSimpleQuestion(sq, false);
              renderStructList.push(...contents);
              renderStructList.push(this.parseLineGap());
            });
          } else {
            questionInfo.number = question.number;
            const datas = this.parseSimpleQuestion(questionInfo, true);
            renderStructList.push(...datas);
            renderStructList.push(this.parseLineGap());
          }
        });
      });

      // 去掉最后一题的间隔行
      // console.log(renderStructList);
      this.renderStructList = renderStructList.slice(0, -1);
    },
    getRichStruct(blocks) {
      return {
        sections: [
          {
            blocks: [...blocks],
          },
        ],
      };
    },
    transformRichJson(richJson) {
      if (!richJson || !richJson.sections) return [];
      let contents = [];
      let curBlock = [];
      const checkNeedSplitSection = (block) => {
        if (block.type !== "image") return false;

        if (block.param) {
          if (block.param.width)
            return block.param.width > this.maxColumnWidth / 2;
          if (block.param.height) return block.param.height > 150;
        }
        return true;
      };

      richJson.sections.forEach((section) => {
        section.blocks.forEach((block) => {
          if (checkNeedSplitSection(block) && curBlock.length) {
            contents.push(this.getRichStruct(curBlock));
            curBlock = [];
          }
          curBlock.push(block);
        });

        if (curBlock.length) {
          contents.push(this.getRichStruct(curBlock));
          curBlock = [];
        }
      });
      return contents;
    },
    parseSimpleQuestion(question, isCommon) {
      let contents = [];

      const tbodys = this.parseTitleOption(
        question.quesBody,
        isCommon ? `${question.number}、` : `${question.subNumber}、`
      );
      contents.push(...tbodys);

      if (question.quesOptions && question.quesOptions.length) {
        question.quesOptions.forEach((op) => {
          const obodys = this.parseTitleOption(
            op.optionBody,
            `${numberToUpperCase(op.number)}、`,
            "option"
          );
          contents.push(...obodys);
        });
      }
      return contents;
    },
    parseDetailTitle(data) {
      let content = this.getRichStruct([
        {
          type: "text",
          value: `${data.cnNum}、${data.name}（共${data.unitCount}小题，满分${data.score}分）`,
        },
      ]);
      return getRichTextModel({
        styles: { width: "100%", fontWeight: 600 },
        content,
      });
    },
    parseTitleOption(richJson, noVal, contType = "content") {
      if (!richJson) return [];
      const bodys = this.transformRichJson(richJson);

      return bodys.map((body, index) => {
        if (index === 0 && noVal) {
          body.sections[0].blocks.unshift({
            type: "text",
            value: noVal,
          });
        }

        return getRichTextModel({
          contType,
          styles: { width: contType !== "option" ? "100%" : "auto" },
          content: body,
        });
      });
    },
    parseLineGap() {
      return getRichTextModel({
        contType: "gap",
        styles: { width: "100%", height: "20px" },
        content: this.getRichStruct([{ type: "text", value: "" }]),
      });
    },
    checkIsMatches(structType) {
      const matchesTypes = ["BANKED_CLOZE", "PARAGRAPH_MATCHING"];
      return matchesTypes.includes(structType);
    },
    buildPrePages() {
      let pages = deepCopy(this.paperTempJson.pages);
      pages[0].columns[0].texts = [];
      pages[0].columns[0].texts.push(...this.renderStructList);
      this.pages = pages;
    },
    buildReleasePages() {
      this.resetRenderStructSize();
      this.buildPageAutoPage();
    },
    resetRenderStructSize() {
      let curOptions = [];
      this.renderStructList.forEach((elem, eindex) => {
        const elemDom = document.getElementById(`rich-text-${elem.id}`);
        elem.w = elemDom.offsetWidth;
        elem.h = elemDom.offsetHeight;

        if (elem.contType !== "option") return;

        curOptions.push(elem);

        // 全选选项逻辑
        const nextElem = this.renderStructList[eindex + 1];
        if (nextElem && nextElem.contType === "option") return;

        curOptions.forEach((optionElem) => {
          optionElem._percent = this.getSizePercent(
            optionElem.w,
            this.maxColumnWidth
          );
        });

        const optionCount = curOptions.length;
        // 奇数选项，全部一行
        if (optionCount % 2 > 0) {
          curOptions.forEach((optionElem) => {
            optionElem._percent = 1;
            optionElem.styles.width = "100%";
          });
          curOptions = [];
          return;
        }

        const percents = curOptions.map((item) => item._percent);
        const maxPercent = maxNum(percents);
        let aveOptionPercent = 1;
        if (optionCount % 4 === 0) {
          aveOptionPercent = this.calcAveOptionPercent(maxPercent);
        } else {
          aveOptionPercent = maxPercent > 0.5 ? 1 : 0.5;
        }
        curOptions.forEach((optionElem) => {
          optionElem._percent = aveOptionPercent;
          optionElem.styles.width = aveOptionPercent * 100 + "%";
        });

        curOptions = [];
      });
    },
    buildPageAutoPage() {
      let pages = [];
      let curPage = null,
        curElem = null;
      let curColumn = null,
        curColumnNo = 0,
        curColumnHeight = 0;
      let curLinePercent = 0;

      const getNextElem = () => {
        return this.renderStructList.shift();
      };

      curElem = getNextElem();
      while (curElem) {
        if (!curPage) {
          curPage = this.getNewPageModel(pages.length);
        }

        if (!curColumn) {
          curColumn = curPage.columns[curColumnNo++];
          curColumnHeight = this.calcInitColumnHeight(curColumn);
        }

        if (
          curElem.contType !== "option" ||
          (curElem.contType === "option" && curElem._percent === 1)
        ) {
          // 非选项，单独占整行
          curLinePercent = 1;
          if (curElem.h + curColumnHeight > this.maxColumnHeight) {
            // 当前栏满了
            if (curColumnNo >= curPage.columnNumber) {
              // 当前页满了
              pages.push(curPage);
              curPage = null;
              curColumnNo = null;
            }
            curColumn = null;
            curColumnHeight = 0;
          } else {
            // 当前栏未满
            curColumnHeight += curElem.h;
            curColumn.texts.push(curElem);
            curElem = getNextElem();
          }
        } else {
          // 选项的处理
          if (curLinePercent + curElem._percent > 1) {
            // 行满了，放下一行
            if (curElem.h + curColumnHeight > this.maxColumnHeight) {
              curLinePercent = 1;
              // 当前栏满了
              if (curColumnNo >= curPage.columnNumber) {
                // 当前页满了
                pages.push(curPage);
                curPage = null;
                curColumnNo = null;
              }
              curColumn = null;
              curColumnHeight = 0;
            } else {
              // 当前栏未满，放下一行
              curLinePercent = curElem._percent;
              curColumnHeight += curElem.h;
              curColumn.texts.push(curElem);
              curElem = getNextElem();
            }
          } else {
            // 行未满，放当前行
            curLinePercent += curElem._percent;
            curColumn.texts.push(curElem);
            curElem = getNextElem();
          }
        }
      }

      if (curPage) {
        pages.push(curPage);
        curPage = null;
      }
      // todo:保证试卷偶数页输出（待需求）

      if (this.paperTempJson.pageConfig.showCover) {
        let coverPage = deepCopy(this.paperTempJson.pages[0]);
        pages.unshift(coverPage);
      }

      this.pages = pages;
    },
    getNewPageModel(pageNo) {
      let contentPages = this.paperTempJson.pages.slice(-2);
      let pNo = pageNo % 2;
      const pageTemp = contentPages[pNo];
      let newPage = getPageModel({
        pageSize: pageTemp.pageSize,
        columnNumber: pageTemp.columnNumber,
        columnGap: pageTemp.columnGap,
        showPageNo: pageTemp.showPageNo,
        showSide: pageTemp.showSide,
      });
      newPage.sides = pageTemp.sides.map((elem) => {
        let nelem = deepCopy(elem);
        nelem.id = getElementId();
        nelem.key = randomCode();

        if (pNo === 1 && nelem.type === "GUTTER") {
          nelem.direction = "right";
        }
        return nelem;
      });
      newPage.columns.forEach((column) => {
        column.texts = [];
      });

      if (pageNo > 1) return newPage;

      newPage.columns.forEach((column, cindex) => {
        column.elements = pageTemp.columns[cindex].elements.map((elem) => {
          let nelem = deepCopy(elem);
          nelem.id = getElementId();
          nelem.key = randomCode();
          if (nelem.elements && nelem.elements.length) {
            nelem.elements.forEach((celem) => {
              celem.id = getElementId();
              celem.key = randomCode();
            });
          }
          return nelem;
        });
        column.texts = [];
      });
      return newPage;
    },
    calcAveOptionPercent(maxPercent) {
      if (maxPercent > 0.5) return 1;
      if (maxPercent > 0.25) return 0.5;
      return 0.25;
    },
    calcInitColumnHeight(column) {
      return calcSum(column.elements.map((item) => item.h));
    },
    getSizePercent(size, fullSize) {
      const rate = size / fullSize;
      if (rate <= 0.25) return 0.25;
      if (rate <= 0.5) return 0.5;
      return 1;
    },
    // img
    loadImg(url) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function () {
          resolve(true);
        };
        img.onerror = function () {
          reject();
        };
        img.src = url;
      });
    },
    getRichJsonImgUrls(richJson) {
      let urls = [];
      if (!richJson) return urls;
      richJson.sections.forEach((section) => {
        section.blocks.forEach((elem) => {
          if (elem.type === "image" && elem.value.startsWith("http")) {
            urls.push(elem.value);
          }
        });
      });
      return urls;
    },
    async waitAllImgLoaded() {
      let imgUrls = [];
      this.renderStructList.forEach((item) => {
        if (item.contType === "gap") return;
        imgUrls.push(...this.getRichJsonImgUrls(item.content));
      });

      // console.log(imgUrls);

      if (!imgUrls.length) return Promise.resolve(true);
      const imgLoads = imgUrls.map((item) => this.loadImg(item));
      const imgLoadResult = await Promise.all(imgLoads).catch(() => {});
      if (imgLoadResult && imgLoadResult.length) {
        return Promise.resolve(true);
      } else {
        return Promise.reject();
      }
    },
    getPreviewTemp() {
      return previewTemp(this.$refs.PaperTemplateView.$el.outerHTML);
    },
    async toDownload() {
      const htmlCont = this.getPreviewTemp();

      if (this.downloading) return;
      this.downloading = true;

      const res = await downloadByApi(() => {
        return paperPdfDownloadApi({
          content: htmlCont,
          templateId: this.curPaperTemp.id,
        });
      }).catch((e) => {
        this.$message.error(e || "下载失败，请重新尝试！");
      });
      this.downloading = false;

      if (!res) return;
      this.$message.success("下载成功！");
    },
  },
};
</script>

<style>
.paper-template-build {
  text-align: center;
}
.paper-template-build-body {
  display: inline-block;
  text-align: initial;
}

.paper-template-build .page-box {
  margin-top: 10px;
  margin-bottom: 10px;
}
</style>
