<template>
  <QuestionBase>
    <template #content>
      <div v-show="isMask" class="mask"></div>
      <div class="stem">
        <RichText v-model="data.stem" :font-size="18" placeholder="输入题干" />
      </div>

      <div class="content">
        <div class="option-wrapper" :style="computedOptionStyle()">
          <template v-for="i in data.property.column_number">
            <div :key="i" class="fill-form">
              <!-- 头部 -->
              <span class="form-header">
                <span v-if="i === 1 && isEnable(data.property.is_enable_number_column)" class="header-serial-number">
                  #
                </span>
                <el-input v-model="data.option_header_list[i - 1].text" placeholder="请输入" />
              </span>

              <span v-for="j in data.property.row_number" :key="j" :class="['form-item', `form-item-${i}-${j}`]">
                <span v-if="i === 1 && isEnable(data.property.is_enable_number_column)" class="serial-number">
                  {{ j }}
                </span>
                <div class="rich">
                  <RichText
                    ref="modelEssay"
                    v-model="data.option_list[j - 1][i - 1].text"
                    :is-fill="true"
                    :toolbar="false"
                    :font-size="12"
                    :wordlimit-num="false"
                    placeholder="请输入"
                  />
                </div>
              </span>
            </div>
            <span
              v-if="i !== data.property.column_number"
              :key="`resize-${i}`"
              class="resize"
              @mousedown="resize($event, i)"
            ></span>
          </template>
        </div>
        <div class="tips">在输入框最前插入##，代表输入框中内容作为正确答案在作答时隐藏。</div>
      </div>
    </template>

    <template #property>
      <el-form :model="data.property">
        <el-form-item label="题号">
          <el-input v-model="data.property.question_number" />
        </el-form-item>
        <el-form-item label-width="45px">
          <el-radio
            v-for="{ value, label } in questionNumberTypeList"
            :key="value"
            v-model="data.other.question_number_type"
            :label="value"
          >
            {{ label }}
          </el-radio>
        </el-form-item>
        <el-form-item label="分值">
          <el-radio
            v-for="{ value, label } in scoreTypeList"
            :key="value"
            v-model="data.property.score_type"
            :label="value"
          >
            {{ label }}
          </el-radio>
        </el-form-item>
        <el-form-item label-width="45px">
          <el-input-number
            v-model="data.property.score"
            :min="0"
            :step="data.property.score_type === scoreTypeList[0].value ? 1 : 0.1"
          />
        </el-form-item>
        <el-form-item label="序号列">
          <el-radio
            v-for="{ value, label } in switchOption"
            :key="value"
            v-model="data.property.is_enable_number_column"
            :label="value"
          >
            {{ label }}
          </el-radio>
        </el-form-item>
        <el-form-item label="列表宽度">
          <el-input-number v-model="data.property.form_width" :min="100" :max="3000" :step="100" />
        </el-form-item>
        <el-form-item label="行数">
          <el-input-number v-model="data.property.row_number" :min="1" :max="30" />
        </el-form-item>
        <el-form-item label="列数">
          <el-input-number v-model="data.property.column_number" :min="2" :max="5" />
        </el-form-item>
      </el-form>
    </template>
  </QuestionBase>
</template>

<script>
import QuestionMixin from '../common/QuestionMixin.js';

import { getRandomNumber } from '@/utils';
import { getTableFillData, getOption } from '@/views/exercise_questions/data/tableFill.js';

export default {
  name: 'TableFillQuestion',
  mixins: [QuestionMixin],
  data() {
    return {
      isMask: false,
      data: getTableFillData(),
    };
  },
  computed: {
    optionList() {
      return JSON.parse(JSON.stringify(this.data.option_list));
    },
  },
  watch: {
    'data.property.row_number': {
      handler(val) {
        if (val > this.data.option_list.length) {
          this.data.option_list.push(Array.from({ length: this.data.property.column_number }, () => getOption()));
        } else if (val < this.data.option_list.length) {
          this.data.option_list.pop();
        }
      },
      immediate: true,
    },
    'data.property.column_number': {
      handler(val) {
        // 列数变化时，需要重新计算每列的宽度
        if (val > this.data.option_header_list.length) {
          let width = 0;
          this.data.option_header_list.forEach((item) => {
            // 平均分配宽度，最小宽度为10
            let w = Math.max((item.width / val) * this.data.option_header_list.length, 10);
            width += item.width - w;
            item.width = w;
          });
          this.data.option_header_list.push({ mark: getRandomNumber(), text: '', width });
        } else if (val < this.data.option_header_list.length) {
          let width = this.data.option_header_list[val].width;
          this.data.option_header_list.pop();
          this.data.option_header_list.forEach((item) => {
            item.width += width / val;
          });
        }

        this.data.option_list = this.data.option_list.map((item) => {
          const arr = [];
          for (let i = 0; i < val; i++) {
            arr.push(item[i] || getOption());
          }
          return arr;
        });
      },
      immediate: true,
    },
    optionList: {
      handler(newVal, oldVal) {
        if (newVal.length === 0 || !oldVal) return;
        if (newVal.length !== oldVal.length || newVal[0].length !== oldVal[0].length) return;
        newVal.forEach((item, i) => {
          item.forEach((li, j) => {
            let newValue = newVal[i][j];
            let oldValue = oldVal[i][j];
            if (newValue.text === oldValue.text) return;
            this.handleOptionChange(newValue, oldValue, i, j);
          });
        });
      },
      immediate: true,
      deep: true,
    },
  },
  methods: {
    /**
     *鼠标拖动更改列宽
     * @param {MouseEvent} e 鼠标事件
     * @param {number} i 选项列表的索引
     */
    resize(e, i) {
      let target = e.target;
      let startX = e.clientX;
      this.isMask = true;

      // 鼠标拖动事件
      document.onmousemove = (e) => {
        const endX = e.clientX;
        let list = this.data.option_header_list;
        // 计算拖动的距离与总宽度的比例
        let w = ((endX - startX) / this.data.property.form_width) * 100;
        // 限制最小宽度为10，最大宽度为总宽度-10
        list[i - 1].width = Math.min(Math.max(10, list[i - 1].width + w), list[i - 1].width + list[i].width - 10);
        list[i].width = Math.min(Math.max(10, list[i].width - w), list[i - 1].width + list[i].width - 10);

        startX = endX;
      };

      // 鼠标松开事件
      document.onmouseup = () => {
        this.isMask = false;
        document.onmousemove = null;
        document.onmouseup = null;
        target.releaseCapture && target.releaseCapture(); // 当不在需要继续获得鼠标消息就要应该调用 ReleaseCapture() 释放掉
      };
      target.setCapture && target.setCapture(); // 该函数在属于当前线程的指定窗口里设置鼠标捕获
      return false;
    },
    // 计算选项样式
    computedOptionStyle() {
      let gridTemplateColumns = this.data.option_header_list.reduce((acc, { width }, i) => {
        if (i === this.data.option_header_list.length - 1) {
          return `${acc}${width}%`;
        }
        return `${acc}${width}% 2px `;
      }, '');

      return {
        gridTemplateColumns,
        width: `${this.data.property.form_width}px`,
      };
    },
    /**
     * 处理选项内容变化
     * @param {object} newValue 新值
     * @param {object} oldValue 旧值
     * @param {number} i 选项列表的索引
     * @param {number} j 选项列表中的索引
     */
    handleOptionChange(newValue, oldValue, i, j) {
      let arr = newValue.text.split(/<p>(.*?)<\/p>/gi).filter((item) => item);
      // 答案数组中的索引
      let answerIndex = this.data.answer.answer_list.findIndex(({ mark }) => mark === newValue.mark);
      if (arr.length === 0) {
        if (answerIndex !== -1) {
          this.data.answer.answer_list.splice(answerIndex, 1);
          answerIndex = -1;
        }
      }
      let isStart = /^##/.test(arr[0]); // 是否以 ## 开头
      if (isStart) {
        this.handleSpecialCharacterStart(arr, answerIndex, newValue.mark, i, j);
      } else {
        let str = arr.join().replace(/<span class="rich-fill".*?>(.*?)<\/span>/gi, '###$1###');
        this.handleFill(str, answerIndex, newValue.mark, i, j);
      }
    },
    /**
     * 处理填空或其他情况
     * @param {string} str 处理后的字符串
     * @param {number} answerIndex 答案数组的索引
     * @param {string} newValueMark 新值的标识
     * @param {number} i 选项列表的索引
     * @param {number} j 选项列表中的索引
     */
    handleFill(str, answerIndex, newValueMark, i, j) {
      let _str = str;
      let start = 0;
      let index = 0;
      let arr = [];
      let matchNum = 0;
      while (index !== -1) {
        index = _str.indexOf('###', start);
        if (index === -1) break;
        matchNum += 1;
        arr.push({ content: _str.slice(start, index), type: 'text', mark: '' });
        if (matchNum % 2 === 0 && arr.length > 0) {
          arr[arr.length - 1].type = 'input';
          let mark = getRandomNumber();
          arr[arr.length - 1].mark = mark;
        }
        start = index + 3;
      }
      let last = _str.slice(start);
      if (last) {
        arr.push({ content: last, type: 'text' });
      }
      let value_list = arr
        .filter(({ type }) => type === 'input')
        .map(({ content, mark }) => ({ value: content, mark }));
      if (answerIndex === -1 && value_list.length > 0) {
        this.data.answer.answer_list.push({
          value_list,
          mark: newValueMark,
        });
      } else if (answerIndex !== -1) {
        this.data.answer.answer_list[answerIndex].value_list = value_list;
      }
      this.data.option_list[i][j].content_list = arr.map(({ content, type, mark }) => {
        return {
          type,
          mark,
          content: type === 'input' ? '' : content,
        };
      });
    },
    /**
     * 处理特殊字符开头
     * @param {array} arr 文本数组
     * @param {number} answerIndex 答案数组的索引
     * @param {string} newValueMark 新值的标识
     * @param {number} i 选项列表的索引
     * @param {number} j 选项列表中的索引
     */
    handleSpecialCharacterStart(arr, answerIndex, newValueMark, i, j) {
      // 去除第一个元素的 ##
      arr[0] = arr[0].slice(2);
      let _arr = arr.map((item) => {
        return item.replace(/<span class="rich-fill".*?>(.*?)<\/span>/gi, '$1');
      });
      let mark = getRandomNumber();

      if (answerIndex === -1) {
        this.data.answer.answer_list.push({
          value_list: [{ mark, value: _arr.join('') }],
          mark: newValueMark,
        });
      } else {
        this.data.answer.answer_list[answerIndex].value_list = [{ mark, value: _arr.join('') }];
      }
      this.data.option_list[i][j].content_list = [
        {
          type: 'fill',
          mark,
          content: '',
        },
      ];
    },
  },
};
</script>

<style lang="scss" scoped>
.mask {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  cursor: col-resize;
}

.content {
  width: calc(100vw - 645px);
  overflow: auto;

  .option-wrapper {
    display: grid;
    grid-auto-flow: column;

    .fill-form {
      display: flex;
      flex-direction: column;

      .form-header {
        display: flex;
        align-items: center;
        height: 40px;
        background-color: $fill-color;

        .header-serial-number {
          padding: 8px 16px;
          font-weight: bold;
        }

        :deep .el-input__inner {
          font-weight: bold;
        }
      }

      .form-item {
        display: flex;
        height: 55px;
        border-bottom: $border;

        .serial-number {
          width: 40px;
          min-width: 40px;
          line-height: 55px;
          text-align: center;
        }

        .rich {
          position: relative;
          width: 100%;
          overflow: auto;
        }

        :deep .tox-tinymce {
          border-width: 0;
        }

        :deep .tox .tox-sidebar-wrap {
          border-width: 0;
        }

        :deep .tox-editor-header {
          display: none;
        }
      }
    }

    .resize {
      width: 2px;
      height: 100%;
      cursor: col-resize;
      background-color: #165dff;
      border-radius: 4px;
    }
  }

  .tips {
    margin-top: 8px;
    font-size: 14px;
    color: #999;
  }
}

.property {
  .el-input-number {
    width: 200px;
  }
}
</style>
