package cn.com.qmth.examcloud.core.questions.service;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.P;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import com.google.gson.Gson;

import cn.com.qmth.examcloud.api.commons.security.bean.User;
import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
import cn.com.qmth.examcloud.commons.exception.StatusException;
import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
import cn.com.qmth.examcloud.core.questions.base.IoUtils;
import cn.com.qmth.examcloud.core.questions.base.Model;
import cn.com.qmth.examcloud.core.questions.base.enums.PaperStatus;
import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
import cn.com.qmth.examcloud.core.questions.base.enums.QuesUnit;
import cn.com.qmth.examcloud.core.questions.base.exception.PaperException;
import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
import cn.com.qmth.examcloud.core.questions.base.word.DocxProcessUtil;
import cn.com.qmth.examcloud.core.questions.dao.CoursePropertyRepo;
import cn.com.qmth.examcloud.core.questions.dao.PaperDetailRepo;
import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
import cn.com.qmth.examcloud.core.questions.dao.PropertyRepo;
import cn.com.qmth.examcloud.core.questions.dao.QuesRepo;
import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
import cn.com.qmth.examcloud.core.questions.dao.entity.ImportPaperCheck;
import cn.com.qmth.examcloud.core.questions.dao.entity.ImportPaperMsg;
import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionType;
import cn.com.qmth.examcloud.core.questions.service.config.SysProperty;
import cn.com.qmth.examcloud.core.questions.service.impl.CourseService;
import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
import cn.com.qmth.examcloud.web.filestorage.FileStoragePathEnvInfo;
import cn.com.qmth.examcloud.web.filestorage.YunPathInfo;

@Service
public class ImportPaperService {

    private static final Logger log = LoggerFactory.getLogger(ImportPaperService.class);

    private static final String DOC_SUFFIX = ".docx";

    private static final String JSON_SUFFIX = ".json";

    @Autowired
    ImportPaperFromJsonService importPaperFromJsonService;

    @Autowired
    PaperRepo paperRepo;

    @Autowired
    PaperDetailRepo paperDetailRepo;

    @Autowired
    PaperDetailUnitRepo paperDetailUnitRepo;

    @Autowired
    QuesService quesService;

    @Autowired
    QuesRepo quesRepo;

    @Autowired
    Gson gson;

    @Autowired
    PaperDetailService paperDetailService;

    @Autowired
    PaperService paperService;

    @Autowired
    CourseService courseService;

    @Autowired
    QuesTypeNameService quesTypeNameService;

//    @Autowired
//    QuesPkgPathRepo quesPkgPathRepo;

    @Autowired
    CoursePropertyRepo coursePropertyRepo;

    @Autowired
    PropertyRepo propertyRepo;

    @Autowired
    QuestionAudioService questionAudioService;

    @Autowired
    SysProperty sysProperty;

    public static final String TEMP_FILE_EXP = "docxExport/";

    private Random random = new Random();

    /**
     * 获取上传文件
     *
     * @param file
     * @return
     */
    public File getUploadFile(CommonsMultipartFile file) {
        File tempFile = new File(DocxProcessUtil.TEMP_FILE_IMP + file.getOriginalFilename());

        OutputStream os;
        try {
            os = new FileOutputStream(tempFile);
        } catch (FileNotFoundException e) {
            log.error(e.getMessage());
            throw new StatusException("500", "文件操作失败！");
        }

        try (InputStream is = file.getInputStream();) {
            IOUtils.copyLarge(is, os);
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            IOUtils.closeQuietly(os);
        }

        return tempFile;
    }

    /**
     * 导入试卷
     *
     * @param paper
     * @param user
     * @param file
     * @return
     * @throws Exception
     */

    public Paper importPaper(Paper paper, User user, File file,UserDataRule ud) throws Exception {
        paperService.checkPaperNameNew(paper.getName(), user.getRootOrgId().toString());
        if (file.getName().endsWith(DOC_SUFFIX)) {
            return processImportPaper(paper, user, file,ud);
        } else if (file.getName().endsWith(JSON_SUFFIX)) {
             return importPaperFromJsonService.processImportPaper(paper, user,
             file);
        } else {
            throw new PaperException("导入文件只支持.docx和.json类型");
        }
    }

    /**
     * 保存导入试卷信息
     *
     * @param paper
     * @param paperDetails
     * @param paperDetailUnits
     * @param importPaperCheck
     * @throws PaperException
     */
    public Paper savePaper(Paper paper, List<PaperDetail> paperDetails, List<PaperDetailUnit> paperDetailUnits,
            List<Question> questions, ImportPaperCheck importPaperCheck) throws Exception {
        Paper tempPaper = null;
        if (!StringUtils.isEmpty(importPaperCheck.getErrorInfo()))
            return null;
        if (paper != null) {
            // 总分校验
            checkTotalScore(paper, paperDetailUnits, importPaperCheck);
            // 大题下小题数量校验
            checkUnitNum(paperDetailUnits, importPaperCheck);
            // 校验答案是否不全
            checkAnswerISfull(paperDetailUnits, importPaperCheck);
            // 如果合并相同的大题
            if (paper.getSameName().equals("1")) {
                paperDetails = mergePaperDetails(paperDetails, paperDetailUnits);
            }
            tempPaper = paperRepo.save(paper);
        }
        if (paperDetails != null && paperDetails.size() > 0) {
            paperDetailRepo.saveAll(paperDetails);
        }
        if (questions != null && questions.size() > 0) {
            for (Question question : questions) {
                String now = CommonUtils.getCurDateTime();
                question.setCreateTime(now);
            }
            quesRepo.saveAll(questions);
        }
        if (paperDetailUnits.size() > 0) {
            paperDetailUnitRepo.saveAll(paperDetailUnits);
            quesTypeNameService.saveQuesTypeName(paperDetailUnits);
        }
        paperService.formatPaper(tempPaper, null);
        return tempPaper;
    }

    /**
     * 合并相同大题
     *
     * @param paperDetails
     * @param paperDetailUnits
     * @return
     */
    private List<PaperDetail> mergePaperDetails(List<PaperDetail> paperDetails,
            List<PaperDetailUnit> paperDetailUnits) {
        List<PaperDetail> mergePaperDetails = new ArrayList<>();
        Set<String> set = new HashSet<>();
        for (PaperDetail paperDetail : paperDetails) {
            if (set.add(paperDetail.getName())) {
                mergePaperDetails.add(paperDetail);
            }
        }
        for (PaperDetail mergePaperDetail : mergePaperDetails) {
            for (PaperDetailUnit mergePaperDetailUnit : paperDetailUnits) {
                if (mergePaperDetailUnit.getPaperDetail().getName().equals(mergePaperDetail.getName())) {
                    mergePaperDetailUnit.setPaperDetail(mergePaperDetail);
                }
            }
        }
        return mergePaperDetails;
    }

    /**
     * 清空试卷对象,加速回收
     */
    public void clearPaper(List<PaperDetail> paperDetails, List<PaperDetailUnit> paperDetailUnits,
            List<Question> questions) {
        paperDetails.clear();
        paperDetailUnits.clear();
        questions.clear();
    }

    /**
     * 处理导入试卷
     *
     * @param paper
     * @param user
     * @param file
     * @return
     * @throws Exception
     */

    public Paper processImportPaper(Paper paper, User user, File file,UserDataRule ud) throws Exception {
        try {
        	if (ud.assertEmptyQueryResult()) {
    			throw new StatusException("没有数据权限");
    		}

            // 得到前台的课程代码
            String courseNo = paper.getCourseNo();

    		Course course = courseService.getCourse(user.getRootOrgId(), courseNo);
    		if(ud.assertNeedQueryRefIds()&&!ud.stringRefIds().contains(course.getId())) {
    			throw new StatusException("没有数据权限");
    		}

			WordprocessingMLPackage wordMLPackage;
			WordprocessingMLPackage tmpWordMlPackage;
			// WordprocessingMLPackage writePkg;
			ImportPaperCheck importPaperCheck = new ImportPaperCheck();
			wordMLPackage = WordprocessingMLPackage.load(file);
			// 初始化图片路径
			DocxProcessUtil.initPkgImage(wordMLPackage);
			// 深拷贝临时pkg与最终写入数据库pkg
			tmpWordMlPackage = DocxProcessUtil.getTmpPackage(wordMLPackage);
			// 获取word文档中所有段落
			List<Object> pList = DocxProcessUtil.getAllElementFromObject(wordMLPackage.getMainDocumentPart(), P.class);
			// 设置试卷
			initPaper(paper, paper.getName(), user);
			// 创建空大题类
			PaperDetail paperDetail = null;
			// 创建大题集合
			List<PaperDetail> paperDetails = new ArrayList<>();
			// 创建小题集合
			List<PaperDetailUnit> paperDetailUnits = new ArrayList<>();
			// 创建试题集合
			List<Question> questions = new ArrayList<>();
			// 大题号
			int mainQuesNum = 0;
			// 小题号
			int subQuesNum = 0;
			// 大题下的小题序号
			int errorQuesNum = 0;
			for (int i = 0; i < pList.size(); i++) {
				P p = (P) pList.get(i);
				String pText = DocxProcessUtil.getPText(p);
				if (StringUtils.isEmpty(pText)) {
					// 跳过空白段落
					continue;
				} else if (isMainQuesHeader(pText)) {
					// 处理大题头信息
					processMainQuesHeader(pList, importPaperCheck.getIndex(), importPaperCheck, courseNo);
					// 校验大题头信息
					if (!checkQuesHeader(importPaperCheck)) {
						throw new PaperException(importPaperCheck.getErrorInfo());
					}
					// 创建大题类
					paperDetail = new PaperDetail();
					// 设置大题类
					initQuesHeader(paper, paperDetail, paperDetails, ++mainQuesNum, importPaperCheck);
					// 设置当前索引,防止多余循环
					i = importPaperCheck.getIndex() - 1;
					errorQuesNum = 0;
				} else if (DocxProcessUtil.isNumPr(p)) {
					// 检测到序列
					importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + ImportPaperMsg.errMsg_10);
					throw new PaperException(importPaperCheck.getErrorInfo());

				} else if ((pText.matches("^\\d{1,}\\.[\\s\\S]*") && !pText.startsWith(ImportPaperMsg.left_bracket))
						|| (isNested(importPaperCheck) && pText.startsWith(ImportPaperMsg.nestedQuestion_start))) {
					if (paperDetail == null) {
						throw new PaperException("导入文件格式有误,必须有大题头信息,且以 [ 开头!");
					}
					++errorQuesNum;
					++subQuesNum;
					// 处理试题
					// 创建小题类和试题类
					PaperDetailUnit paperDetailUnit = new PaperDetailUnit();
					Question question = new Question();
					// 初始化小题类和试题类
					initPaperDetail(paper, paperDetail, paperDetailUnit, question, subQuesNum, importPaperCheck);
					// 处理客观题
					if (importPaperCheck.getQuesType().equals(ImportPaperMsg.singleSelection)
							|| importPaperCheck.getQuesType().equals(ImportPaperMsg.multipleSelection)) {
						// 处理题干
						processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
								tmpWordMlPackage, errorQuesNum);
						// 处理选项
						processQuesOption(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
								tmpWordMlPackage, errorQuesNum);
						// 处理尾信息
						processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, question, paperDetailUnit,
								importPaperCheck, tmpWordMlPackage, false, paper, errorQuesNum);
						// 处理选择题的option--chenken 20170425
						processSelectOption(question);
					} else if (importPaperCheck.getQuesType().equals(ImportPaperMsg.nestedQuestion_word)) {
						// 处理套题
						processNestedQues(pList, importPaperCheck.getIndex(), question, paperDetailUnit,
								importPaperCheck, tmpWordMlPackage, paper);
					} else {
						// 处理其他题型
						processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
								tmpWordMlPackage, errorQuesNum);
						processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, question, paperDetailUnit,
								importPaperCheck, tmpWordMlPackage, false, paper, errorQuesNum);
						// 填空题空格校验
						if (question.getQuestionType().getName().equals(QuesStructType.FILL_BLANK_QUESTION.getName())) {
							if (!StringUtils.isBlank(question.getQuesAnswer())) {
								processFill(question, paperDetailUnit, importPaperCheck, subQuesNum, errorQuesNum);
							}
						}
					}
					// 设置WordMlPackage二进制数据
					//                byte[] pkgByte = getZipResource(wordMLPackage, question);
					//                QuestionPkgPath quesPkgPath = quesPkgPathRepo.save(new QuestionPkgPath(pkgByte));
					//                question.setQuesPkgPathId(quesPkgPath.getId());
					//                pkgByte = null;
					//                quesPkgPath.setQuesPkg(null);
					// 设置question与Unit集合数据
					question.setCourse(paper.getCourse());
					question.setOrgId(user.getRootOrgId().toString());
					questions.add(question);
					paperDetailUnits.add(paperDetailUnit);
					// 设置当前索引,防止多余循环
					i = importPaperCheck.getIndex() - 1;
				} else if (paperDetail == null) {
					throw new PaperException("导入文件格式有误,必须有大题头信息,且以[试题分类]开头!");
				} else {
					String errorMsg = pText.length() > 10 ? pText.substring(0, 10) : pText;
					if (pText.startsWith(ImportPaperMsg.left_bracket)) {
						throw new PaperException(
								errorMsg + "，标签格式不正确！正确标签格式：[套题]、[套题数量]、[小题分数]、[答案]、[难度]、[一级属性]、[二级属性]、[公开度]、[小题型]");
					}
					throw new PaperException(
							importPaperCheck.getQuesName() + "：“" + errorMsg + "”" + ImportPaperMsg.errMsg_12);
				}
				if (!StringUtils.isEmpty(importPaperCheck.getErrorInfo())) {
					throw new PaperException(importPaperCheck.getErrorInfo());
				}
			}
			if (paperDetails.size() == 0 || paperDetailUnits.size() == 0) {
				throw new PaperException("导入文件格式有误!");
			}
			paper.setPaperDetailCount(mainQuesNum);
			// 保存导入试卷信息
			paper = savePaper(paper, paperDetails, paperDetailUnits, questions, importPaperCheck);
			clearPaper(paperDetails, paperDetailUnits, questions);
			wordMLPackage = null;
			tmpWordMlPackage = null;
		} finally {
			FileUtils.deleteQuietly(file);
		}
        return paper;
    }

//    private byte[] getZipResource(WordprocessingMLPackage wordMLPackage, Question question) throws Exception {
//        List<String> wordXmls = getWordXmlByQuestion(question);
//        List<Question> subQuestions = question.getSubQuestions();
//        if (subQuestions != null && subQuestions.size() > 0) {
//            for (Question subQuestion : subQuestions) {
//                wordXmls.addAll(getWordXmlByQuestion(subQuestion));
//            }
//        }
//        return DocxProcessUtil.getWordBytesByQuestion(wordMLPackage, wordXmls);
//    }

    public List<String> getWordXmlByQuestion(Question question) {
        List<String> wordXmls = new ArrayList<>();
        wordXmls.add(question.getQuesBodyWord());
        List<QuesOption> options = question.getQuesOptions();
        if (options != null && options.size() > 0) {
            for (QuesOption quesOption : options) {
                wordXmls.add(quesOption.getOptionBodyWord());
            }
        }
        wordXmls.add(question.getQuesAnswerWord());
        return wordXmls;
    }

    public boolean isMainQuesHeader(String pText) {
        /*
         * return pText.startsWith(ImportPaperMsg.left_bracket) &&
         * !pText.startsWith(ImportPaperMsg.answer_word) &&
         * !pText.startsWith(ImportPaperMsg.score_word) &&
         * !pText.startsWith(ImportPaperMsg.subQuestionType_word) &&
         * !pText.startsWith(ImportPaperMsg.nestedQuestion_start) &&
         * !pText.startsWith(ImportPaperMsg.nestedQuestion_number);
         */
        return pText.startsWith(ImportPaperMsg.questionClassify_word)
                || pText.startsWith(ImportPaperMsg.questionType_word)
                || pText.startsWith(ImportPaperMsg.questionName_word)
                || pText.startsWith(ImportPaperMsg.questionNum_word) || pText.startsWith(ImportPaperMsg.score_word);
    }

    public boolean isHeader(String pText) {
        return pText.startsWith(ImportPaperMsg.questionClassify_word)
                || pText.startsWith(ImportPaperMsg.questionType_word)
                || pText.startsWith(ImportPaperMsg.questionName_word)
                || pText.startsWith(ImportPaperMsg.questionNum_word) || pText.startsWith(ImportPaperMsg.score_word)
                || pText.startsWith(ImportPaperMsg.answer_word) || pText.startsWith(ImportPaperMsg.subQuestionType_word)
                || pText.startsWith(ImportPaperMsg.subQuesScore_word)
                || pText.startsWith(ImportPaperMsg.nestedQuestion_start)
                || pText.startsWith(ImportPaperMsg.nestedQuestion_number)
                || pText.startsWith(ImportPaperMsg.left_bracket);
    }

    /**
     * 初始化试卷信息
     *
     * @param paper
     * @param paperName
     * @param user
     */
    public void initPaper(Paper paper, String paperName, User user) {
        paper.setName(paperName);
        paper.setTitle(paperName);
        paper.setPaperType(PaperType.IMPORT);
        paper.setPaperStatus(PaperStatus.DRAFT);
        paper.setOrgId(user.getRootOrgId().toString());
        paper.setCreator(user.getDisplayName());
        Course course = courseService.getCourse(user.getRootOrgId(), paper.getCourseNo());
        paper.setCourse(course);
        paper.setCourseName(course.getName());
        paper.setCreateTime(CommonUtils.getCurDateTime());
    }

    /**
     * 初始化大题头信息
     *
     * @param paper
     * @param paperDetail
     * @param paperDetails
     * @param mainQuesNum
     * @param importPaperCheck
     */
    public void initQuesHeader(Paper paper, PaperDetail paperDetail, List<PaperDetail> paperDetails, int mainQuesNum,
            ImportPaperCheck importPaperCheck) {

        paperDetail.setPaper(paper);
        // 设置大题信息
        paperDetail.setName(importPaperCheck.getQuesName());
        paperDetail.setUnitCount(Integer.parseInt(importPaperCheck.getQuesCount()));
        paperDetail.setNumber(mainQuesNum);
        paperDetail.setCreator(paper.getCreator());
        paperDetails.add(paperDetail);

    }

    /**
     * 初始化试卷明细和试题
     *
     * @param paperDetail
     * @param paperDetailUnit
     * @param question
     * @param subQuesNum
     * @param importPaperCheck
     */
    public void initPaperDetail(Paper paper, PaperDetail paperDetail, PaperDetailUnit paperDetailUnit,
            Question question, int subQuesNum, ImportPaperCheck importPaperCheck) {

        // 重置importPaperCheck
        importPaperCheck.setNestedHeadNumber(0);
        question.setQuestionType(getQuesStructType(importPaperCheck.getQuesType()));
        question.setScore(Double.parseDouble(importPaperCheck.getQuesScore()));
        paperDetailUnit.setNumber(subQuesNum);
        paperDetailUnit.setQuestion(question);
        paperDetailUnit.setPaperDetail(paperDetail);
        paperDetailUnit.setQuestionType(getQuesStructType(importPaperCheck.getQuesType()));
        paperDetailUnit.setScore(Double.parseDouble(importPaperCheck.getQuesScore()));
        paperDetailUnit.setCreator(paper.getCreator());
        paperDetailUnit.setPaper(paper);
        paperDetailUnit.setPaperType(PaperType.IMPORT);
    }

    /**
     * 校验试题头标识
     *
     * @param importPaperCheck
     * @return
     */
    public boolean checkQuesHeader(ImportPaperCheck importPaperCheck) {

        String tmpErrorInfo = "";
        if (StringUtils.isEmpty(importPaperCheck.getQuesGroup())) {
            tmpErrorInfo += ImportPaperMsg.errMsg_04;

        }
        if (StringUtils.isEmpty(importPaperCheck.getQuesType())) {
            tmpErrorInfo += ImportPaperMsg.errMsg_05;

        }
        if (StringUtils.isEmpty(importPaperCheck.getQuesName())) {
            tmpErrorInfo += ImportPaperMsg.errMsg_06;

        }
        if (StringUtils.isEmpty(importPaperCheck.getQuesCount())) {
            tmpErrorInfo += ImportPaperMsg.errMsg_07;

        }
        if (StringUtils.isEmpty(importPaperCheck.getQuesScore())) {
            tmpErrorInfo += ImportPaperMsg.errMsg_08;
        }
        if (!StringUtils.isEmpty(importPaperCheck.getQuesType())
                && !CommonUtils.checkQuesType(importPaperCheck.getQuesType())) {
            tmpErrorInfo += ImportPaperMsg.errMsg_09;
        }
        if (StringUtils.isEmpty(tmpErrorInfo)) {
            return true;
        } else {
            importPaperCheck.setErrorInfo(tmpErrorInfo);
            return false;
        }
    }

    /**
     * 处理大题头信息
     *
     * @param pList
     * @param index
     * @param importPaperCheck
     * @throws PaperException
     */
    public void processMainQuesHeader(List<Object> pList, int index, ImportPaperCheck importPaperCheck, String courseNo)
            throws PaperException {
        int i = 0;
        for (i = index; i < pList.size(); i++) {
            P pFirst = (P) pList.get(i);
            String tmpTextFirst = DocxProcessUtil.getPText(pFirst);
            if (StringUtils.isBlank(tmpTextFirst)) {
                continue;
            }
            if (!tmpTextFirst.startsWith(ImportPaperMsg.questionClassify_word)) {
                throw new PaperException("导入文件格式有误,必须有大题头信息,且以[试题分类]开头!");
            }
            break;
        }
        for (i = index; i < pList.size(); i++) {
            P pHeader = (P) pList.get(i);
            P pNextHeader = (P) pList.get(i + 1);
            String tmpText = DocxProcessUtil.getPText(pHeader);
            String nextTmpText = DocxProcessUtil.getPText(pNextHeader);
            if (tmpText.startsWith(ImportPaperMsg.questionClassify_word)) {
                if (!nextTmpText.startsWith(ImportPaperMsg.questionType_word)) {
                    importPaperCheck.setErrorInfo("[试题分类]的下一题头应该是[题型],当前读到的信息为:“" + nextTmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                List<String> paperList = Stream.of(tmpText.split("_")).collect(Collectors.toList());
                // 获取[试题分类]中的课程代码
                if (paperList.size() != 2) {
                    importPaperCheck.setErrorInfo("[试题分类]的格式不正确或者是内容为空");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                String impCourseCode = paperList.get(1);
                if (!courseNo.equals(impCourseCode)) {
                    importPaperCheck.setErrorInfo("[试题分类]中的课程代码与指定的课程代码不一样，当前读到的信息为:“" + tmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                importPaperCheck.setQuesGroup(getContent(tmpText, ImportPaperMsg.questionClassify_word));
            } else if (tmpText.startsWith(ImportPaperMsg.questionType_word)) {
                if (!nextTmpText.startsWith(ImportPaperMsg.questionName_word)) {
                    importPaperCheck.setErrorInfo("[题型]的下一题头应该是[大题名称],当前读到的信息为:“" + nextTmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                importPaperCheck.setQuesType(getContent(tmpText, ImportPaperMsg.questionType_word));
            } else if (tmpText.startsWith(ImportPaperMsg.questionName_word)) {
                if (!nextTmpText.startsWith(ImportPaperMsg.questionNum_word)) {
                    importPaperCheck.setErrorInfo("[大题名称]的下一题头应该是[题目数量],当前读到的信息为:“" + nextTmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                importPaperCheck.setQuesName(getContent(tmpText, ImportPaperMsg.questionName_word));
            } else if (tmpText.startsWith(ImportPaperMsg.questionNum_word)) {
                if (!CommonUtils.isInteger(getContent(tmpText, ImportPaperMsg.questionNum_word))) {
                    importPaperCheck.setErrorInfo("[题目数量]的格式不正确");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                if (!nextTmpText.startsWith(ImportPaperMsg.score_word)) {
                    importPaperCheck.setErrorInfo("[题目数量]的下一题头应该是[分数],当前读到的信息为:“" + nextTmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                importPaperCheck.setQuesCount(getContent(tmpText, ImportPaperMsg.questionNum_word));
            } else if (tmpText.startsWith(ImportPaperMsg.score_word)) {
                if (!CommonUtils.isInteger(getContent(tmpText, ImportPaperMsg.score_word))) {
                    importPaperCheck.setErrorInfo("[分数]的格式不正确");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                importPaperCheck.setQuesScore(getContent(tmpText, ImportPaperMsg.score_word));
            } else if (StringUtils.isEmpty(tmpText)) {
                continue;
            } else {
                break;
            }
        }
        importPaperCheck.setIndex(i);
    }

    /**
     * 处理套题的头信息
     *
     * @param pList
     * @param index
     * @param subQuesNum
     * @param question
     * @param importPaperCheck
     * @throws Exception
     */
    public void processNestedQuestionHead(List<Object> pList, int index, int subQuesNum, Question question,
            ImportPaperCheck importPaperCheck, PaperDetailUnit paperDetailUnit) throws Exception {
        int i = 0;
        for (i = index; i < pList.size(); i++) {
            P pHeader = (P) pList.get(i);
            P pNextHeader = (P) pList.get(i + 1);
            String tmpText = DocxProcessUtil.getPText(pHeader);
            String nextTmpText = DocxProcessUtil.getPText(pNextHeader);
            if (StringUtils.isEmpty(tmpText)) {
                // 跳过空白段落
                continue;
            } else if (tmpText.startsWith(ImportPaperMsg.nestedQuestion_start)) {
                // 判断【套题】后面的值
                if (StringUtils.isBlank(getContent(tmpText, ImportPaperMsg.nestedQuestion_start))) {
                    importPaperCheck.setErrorInfo(paperDetailUnit.getPaperDetail().getName()
                            + "中“[套题]：”的后面没有值,当前读到的信息为:“" + tmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                Pattern pattern = Pattern.compile("[0-9]*");
                boolean isHeadNumber = pattern.matcher(getContent(tmpText, ImportPaperMsg.nestedQuestion_start))
                        .matches();
                if (!isHeadNumber) {
                    importPaperCheck.setErrorInfo("[套题]后面应该为整数,当前读到的信息为:“" + tmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                // 判断是否含有【套题数量】标签
                if (!nextTmpText.startsWith(ImportPaperMsg.nestedQuestion_number)) {
                    // 如果读取的错误信息太长，截取前50个字符。
                    if (nextTmpText.length() > 50) {
                        nextTmpText = nextTmpText.substring(0, 50) + "...";
                    }
                    importPaperCheck.setErrorInfo("[大题名称]：" + paperDetailUnit.getPaperDetail().getName() + "," + tmpText
                            + "的下一题头应该是[套题数量],当前读到的信息为:“" + nextTmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                // 判断【套题数量】后面的值
                if (StringUtils.isBlank(getContent(nextTmpText, ImportPaperMsg.nestedQuestion_number))) {
                    importPaperCheck.setErrorInfo("[套题数量]的后面没有值,当前读到的信息为:“" + nextTmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                boolean isNumber = pattern.matcher(getContent(nextTmpText, ImportPaperMsg.nestedQuestion_number))
                        .matches();

                if (!isNumber) {
                    importPaperCheck.setErrorInfo("[套题数量]应该为整数,当前读到的信息为:“" + nextTmpText + "”,请检查word中格式问题");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                importPaperCheck.setNestedHeadNumber(
                        Integer.parseInt(getContent(tmpText, ImportPaperMsg.nestedQuestion_start)));
                importPaperCheck.setNestedQuesNumber(
                        Integer.parseInt(getContent(nextTmpText, ImportPaperMsg.nestedQuestion_number)));
            } else {
                importPaperCheck.setIndex(i + 1);
                break;
            }
        }
    }

    /**
     * 处理题干信息
     *
     * @param pList
     * @param index
     * @param question
     * @param importPaperCheck
     * @param wordMLPackage
     * @throws Exception
     */
    public void processQuesBody(List<Object> pList, int index, int subQuesNum, Question question,
            ImportPaperCheck importPaperCheck, WordprocessingMLPackage wordMLPackage, int errorQuesNum)
            throws Exception {
        // 定义题干wordml和html
        StringBuilder quesBodyWordMl = new StringBuilder();
        StringBuilder quesBodyHtml = new StringBuilder();
        boolean hasSeq=false;
        int i = 0;
        for (i = index; i < pList.size(); i++) {
            P pBody = (P) pList.get(i);
            String tmpText = DocxProcessUtil.getPText(pBody);
            if (StringUtils.isEmpty(tmpText) && DocxProcessUtil.isText(pBody)) {
                // 跳过空白段落
                continue;
            } else if (DocxProcessUtil.isNumPr(pBody)) {
                // 检测到序列
                importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + ImportPaperMsg.errMsg_10);
                throw new PaperException(importPaperCheck.getErrorInfo());

            } else if (tmpText.matches("^\\d{1,}\\.[\\s\\S]*")) {
            	if(hasSeq) {
            		// 检测到序列
            		if (subQuesNum == 0) {
                        if (importPaperCheck.getNestedHeadNumber() != 0) {
                            importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                                    + importPaperCheck.getNestedHeadNumber() + "个题" + ImportPaperMsg.errMsg_14);
                        } else {
                            importPaperCheck.setErrorInfo(
                                    getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_14);
                        }
                    } else {
                        if (importPaperCheck.getNestedHeadNumber() != 0) {
                            importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                                    + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + ImportPaperMsg.errMsg_14);
                        } else {
                            importPaperCheck.setErrorInfo(
                                    getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_14);
                        }
                    }
                    throw new PaperException(importPaperCheck.getErrorInfo());
            	}else {
            		hasSeq=true;
            	}
                // 题干第一段
                // 过滤题干标题
                pBody = DocxProcessUtil.formatP(pBody, QuesUnit.QUES_BODY);
                String tmpWordMl = DocxProcessUtil.getPWordMl(pBody);
                tmpWordMl = DocxProcessUtil.formatPWordMl(tmpWordMl);
                quesBodyWordMl.append(tmpWordMl);
                quesBodyHtml.append(DocxProcessUtil.docx2Html(tmpWordMl, wordMLPackage));
            } else if (tmpText.matches("^[a-zA-Z]\\.[\\s\\S]*") || isHeader(tmpText)) {
                // 检测到选项或其他特殊段落直接退出
                break;
            } else {
                // 题干普通段落
                String tmpWordMl = DocxProcessUtil.getPWordMl(pBody);
                tmpWordMl = DocxProcessUtil.formatPWordMl(tmpWordMl);
                quesBodyWordMl.append(tmpWordMl);
                quesBodyHtml.append(DocxProcessUtil.docx2Html(tmpWordMl, wordMLPackage));
            }
        }
        if (StringUtils.isEmpty(quesBodyHtml) || StringUtils.isEmpty(quesBodyWordMl)) {
            if (subQuesNum == 0) {
                if (importPaperCheck.getNestedHeadNumber() != 0) {
                    importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                            + importPaperCheck.getNestedHeadNumber() + "个题" + ImportPaperMsg.errMsg_01);
                } else {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_01);
                }
            } else {
                if (importPaperCheck.getNestedHeadNumber() != 0) {
                    importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                            + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + ImportPaperMsg.errMsg_01);
                } else {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_01);
                }
            }
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
        importPaperCheck.setIndex(i);
        question.setQuesBody(quesBodyHtml.toString());
        question.setQuesBodyWord(quesBodyWordMl.toString());
    }

    /**
     * 处理题目选项
     *
     * @param pList
     * @param index
     * @param subQuesNum
     * @param question
     * @param importPaperCheck
     * @param wordMLPackage
     * @return
     * @throws Exception
     */
    public String processQuesOption(List<Object> pList, int index, int subQuesNum, Question question,
            ImportPaperCheck importPaperCheck, WordprocessingMLPackage wordMLPackage, int errorQuesNum)
            throws Exception {

        // 定义选项集合
        List<QuesOption> quesOptions = new ArrayList<>();
        // 定义选项数量
        int optionCount = 0;
        // 当前所处的选项
        QuesOption current = null;
        int i = 0;
        int number = 0;
        for (i = index; i < pList.size(); i++) {
            P pOption = (P) pList.get(i);
            String tmpText = DocxProcessUtil.getPText(pOption);
            if (StringUtils.isEmpty(tmpText) && DocxProcessUtil.isText(pOption)) {
                // 跳过空白段落
                continue;
            } else if (DocxProcessUtil.isNumPr(pOption)) {
                // 检测到序列
                if (importPaperCheck.getNestedHeadNumber() != 0) {
                    importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                            + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + ImportPaperMsg.errMsg_11);
                } else {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_11);
                }
                throw new PaperException(importPaperCheck.getErrorInfo());
            } else if (tmpText.matches("^[a-zA-Z]\\.[\\s\\S]*")) {
                // 检测到选项开始段落
                // 创建试题选项
                number++;
                current = new QuesOption();
                int optionNumber = DocxProcessUtil.getOptionNum(tmpText);
                if (optionNumber != number || "abcdefghijklmnopqrstuvwxyz".indexOf(tmpText.substring(0, 1)) > -1) {
                    String errorMsg = tmpText.length() > 10 ? tmpText.substring(0, 10) : tmpText;
                    if (importPaperCheck.getNestedHeadNumber() != 0) {
                        importPaperCheck.setErrorInfo(
                                importPaperCheck.getQuesName() + "第" + importPaperCheck.getNestedHeadNumber() + "个题"
                                        + errorQuesNum + ":“" + errorMsg + "”" + ImportPaperMsg.errMsg_13);
                    } else {
                        importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum)
                                + ":“" + errorMsg + "”" + ImportPaperMsg.errMsg_13);
                    }
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                current.setNumber(String.valueOf(optionNumber));
                current.setOptionBody("");
                current.setOptionBodyWord("");
                quesOptions.add(current);
                optionCount++;
                // 过滤选项标题
                pOption = DocxProcessUtil.formatP(pOption, QuesUnit.QUES_OPTION);
                String tmpWordMl = DocxProcessUtil.getPWordMl(pOption);
                current.setOptionBody(current.getOptionBody() + DocxProcessUtil.docx2Html(tmpWordMl, wordMLPackage));
                current.setOptionBodyWord(current.getOptionBodyWord() + DocxProcessUtil.formatPWordMl(tmpWordMl));
            } else if (isHeader(tmpText)) {
                // 非选项的其他内容直接退出
                break;
            } else if (current != null) {
                // 选项的其他段落
                String tmpWordMl = DocxProcessUtil.getPWordMl(pOption);
                current.setOptionBody(current.getOptionBody() + DocxProcessUtil.docx2Html(tmpWordMl, wordMLPackage));
                current.setOptionBodyWord(current.getOptionBodyWord() + DocxProcessUtil.formatPWordMl(tmpWordMl));
            } else {
                break;
            }
        }
        importPaperCheck.setIndex(i);
        if (optionCount < 2) {
            if (importPaperCheck.getNestedHeadNumber() != 0) {
                importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                        + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + "中选项格式不正确或有缺失\n");
            } else {
                importPaperCheck
                        .setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + "中选项格式不正确或有缺失\n");
            }
            throw new PaperException(importPaperCheck.getErrorInfo());
        } else {
            question.setQuesOptions(quesOptions);
        }
        return importPaperCheck.getErrorInfo();
    }

    /**
     * 处理小题尾信息
     *
     * @param pList
     * @param index
     * @param subQuesNum
     * @param question
     * @param paperDetailUnit
     * @param importPaperCheck
     */
    public void processQuesTail(List<Object> pList, int index, int subQuesNum, Question question,
            PaperDetailUnit paperDetailUnit, ImportPaperCheck importPaperCheck, WordprocessingMLPackage wordMLPackage,
            boolean isNested, Paper paper, int errorQuesNum) throws Exception {

        StringBuilder answerWordML = new StringBuilder("");
        StringBuilder answerHTML = new StringBuilder("");
        String quesScore = null;
        // 定义一级属性
        String firstProperty = null;
        // 定义二级级属性
        String secondProperty = null;
        // 定义难度
        Double difficulty = null;
        // 定义公开度
        Boolean publicity = null;
        // 定义试题属性集合
        List<QuesProperty> quesProperties = new ArrayList<>();
        // Map<String, String> quesParams = new HashMap<>();
        int i = 0;
        // 是否刚刚检测到答案内容
        boolean answerStart = false;
        boolean hasAnswer = false;
        for (i = index; i < pList.size(); i++) {
            P pAnswer = (P) pList.get(i);
            String tmpText = DocxProcessUtil.getPText(pAnswer);
            if (StringUtils.isEmpty(tmpText) && DocxProcessUtil.isText(pAnswer)) {
                // 跳过空白段落
                continue;
            } else if (tmpText.startsWith(ImportPaperMsg.answer_word)) {
                // 检测到答案开始段落
                pAnswer = DocxProcessUtil.formatP(pAnswer, QuesUnit.QUES_ANSWER);
                String tmpWordMl = DocxProcessUtil.getPWordMl(pAnswer);
                if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                        || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                    // 校验单选多选答案
                    checkSelectAnswer(question, pList.get(i).toString(), importPaperCheck, errorQuesNum);
                    answerWordML.append(DocxProcessUtil.formatPWordMl(tmpWordMl));
                    answerHTML.append(getContent(tmpText, ImportPaperMsg.answer_word));
                } else if (question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
                    // 校验判断题答案
                    String answerText = getContent(tmpText, ImportPaperMsg.answer_word);
                    checkBoolAnswer(answerText, importPaperCheck, errorQuesNum);
                    answerWordML.append(DocxProcessUtil.formatPWordMl(tmpWordMl));
                    answerHTML.append(answerText);
                } else {
                    if (!DocxProcessUtil.isTextFromP(DocxProcessUtil.docx2Html(tmpWordMl, wordMLPackage))) {
                        answerWordML.append(DocxProcessUtil.formatPWordMl(tmpWordMl));
                        answerHTML.append(DocxProcessUtil.docx2Html(tmpWordMl, wordMLPackage));
                    }
                }
                hasAnswer = true;
                answerStart = true;
            } else if (tmpText.startsWith(ImportPaperMsg.subQuesScore_word)) {
                // 检测到分数开始段落
                quesScore = getContent(tmpText, ImportPaperMsg.subQuesScore_word);
                if (!CommonUtils.isInteger(quesScore)) {
                    if (importPaperCheck.getNestedHeadNumber() == 0) {
                        importPaperCheck.setErrorInfo(
                                "[大题名称]：" + importPaperCheck.getQuesName() + "第" + errorQuesNum + "题中[小题分数]格式不对或没有值");
                    } else {
                        importPaperCheck.setErrorInfo("[大题名称]：" + importPaperCheck.getQuesName() + "第"
                                + importPaperCheck.getNestedHeadNumber() + "个套题中,第" + errorQuesNum
                                + "题中[小题分数]格式不对或没有值");
                    }
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                answerStart = false;
            }
            // -------------------------------------------------------------------------------------------------------
            else if (tmpText.startsWith(ImportPaperMsg.first_property)) {
                // 检测到一级属性开始段落
                firstProperty = getContent(tmpText, ImportPaperMsg.first_property);
                if (StringUtils.isBlank(firstProperty)) {
                    firstProperty = "";
                }
                answerStart = false;
            } else if (tmpText.startsWith(ImportPaperMsg.second_property)) {
                // 检测到二级属性开始段落
                secondProperty = getContent(tmpText, ImportPaperMsg.second_property);
                if (StringUtils.isBlank(secondProperty)) {
                    secondProperty = "";
                }
                answerStart = false;
            } else if (tmpText.startsWith(ImportPaperMsg.difficulty)) {
                // 检测到难度开始段落 difficultyDegree
                String dif = getContent(tmpText, ImportPaperMsg.difficulty);

                difficulty = checkDifficulty(dif, importPaperCheck, errorQuesNum, question);
                question.setDifficultyDegree(difficulty);
                answerStart = false;
            } else if (tmpText.startsWith(ImportPaperMsg.publicity)) {
                // 检测到公安度开始段落
                String pub = getContent(tmpText, ImportPaperMsg.publicity);

                publicity = checkPublicity(pub, importPaperCheck, errorQuesNum);
                question.setPublicity(publicity);
                answerStart = false;
            }
            // *******************************************************************************************************
            else if (isHeader(tmpText) || tmpText.matches("^\\d{1,}\\.[\\s\\S]*")) {
                // 检测到其他特殊段落或下一题直接退出
                break;
            } else if (answerStart) {
                String tmpWordMl = DocxProcessUtil.getPWordMl(pAnswer);
                answerWordML.append(DocxProcessUtil.formatPWordMl(tmpWordMl));
                answerHTML.append(DocxProcessUtil.docx2Html(tmpWordMl, wordMLPackage));
                if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                        || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + pList.get(i).toString());
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }else if(question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION){
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,应为:正确或错误");
                }
            } else {
                break;
            }
        }
        importPaperCheck.setIndex(i);
        // 如果没有答案字段，抛出异常
        if (!hasAnswer) {
            if (paperDetailUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                importPaperCheck.setErrorInfo(paperDetailUnit.getPaperDetail().getName() + "中，第"
                        + paperDetailUnit.getNumber() + "个套题的" + errorQuesNum + "小题中,缺失“[答案]”");
            } else {
                importPaperCheck
                        .setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + "中,缺失“[答案]”");
            }
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
        // 设置答案
        if (StringUtils.isNotEmpty(answerHTML)) {
            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                    || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
                    ||question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
                question.setQuesAnswer(answerHTML.toString().replaceAll(" ", ""));
            }else {
                question.setQuesAnswer(answerHTML.toString());
            }
            question.setQuesAnswerWord(answerWordML.toString());
        } else {
            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                    || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
                    ||question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
                question.setQuesAnswer("");
            }else {
                question.setQuesAnswer("<p></p>");
            }
            question.setQuesAnswerWord(answerWordML.toString());
        }

        /*
         * // 设置预设分数 if (quesScore != null &&
         * quesScore.matches("^\\d+(\\.\\d+)?$")) {
         * question.setScore(Double.parseDouble(quesScore)); } // 一般大题明细需要设置分数
         * if (!isNested && quesScore != null &&
         * quesScore.matches("^\\d+(\\.\\d+)?$")) {
         * paperDetailUnit.setScore(Double.parseDouble(quesScore)); }
         */
        if (quesScore != null) {
            question.setScore(Double.parseDouble(quesScore));
        }
        // 一般大题明细需要设置分数
        if (!isNested && quesScore != null) {
            paperDetailUnit.setScore(Double.parseDouble(quesScore));
        }
        // 校验小题尾信息是否含有"一节属性"，"二级属性","难度","公开"
        checkAttributeIsFull(firstProperty, secondProperty, difficulty, publicity, importPaperCheck, errorQuesNum);
        // 试题一级属性与二级属性校验
        checkProperty(firstProperty, secondProperty, importPaperCheck, errorQuesNum, paper, quesProperties);
        question.setQuesProperties(quesProperties);
    }

    /**
     * 校验一级属性与二级属性，并保存到试题中
     *
     * @param firstProperty
     * @param secondProperty
     * @param importPaperCheck
     * @param subQuesNum
     * @param paper
     * @param quesProperties
     * @throws Exception
     */
    private void checkProperty(String firstProperty, String secondProperty, ImportPaperCheck importPaperCheck,
            int subQuesNum, Paper paper, List<QuesProperty> quesProperties) throws Exception {
        // 一级属性为空，二级属性有值
        if (StringUtils.isBlank(firstProperty) && StringUtils.isNotBlank(secondProperty)) {
                importPaperCheck
                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值为空,请检查");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
        // 一级属性，二级属性都有值
        else if (StringUtils.isNotBlank(firstProperty) && StringUtils.isNotBlank(secondProperty)) {
            boolean isFirstEmpty = true;
            boolean isSecondEmpty = true;
            // 根据课程查询所有课程属性树
            List<CourseProperty> courseProperties = coursePropertyRepo
                    .findByCourseCodeAndEnable(paper.getCourse().getCode(), true);
            if (courseProperties == null || courseProperties.size() < 1) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,没有设置课程属性结构，请先设置课程属性结构");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }

            for (CourseProperty courseProperty : courseProperties) {
                // 查询一级属性是否存在
                Property proParent = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
                		Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);

                // 存在一级属性
                if (proParent != null) {
                    isFirstEmpty = false;

                    // 查询二级属性
                    Property proSon = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
                    		Long.valueOf(paper.getOrgId()), courseProperty.getId(), proParent.getId(), secondProperty);

                    // 存在二级属性
                    if (proSon != null) {
                        isSecondEmpty = false;
                        // 保存一级和二级属性
                        QuesProperty quesProperty = new QuesProperty(proParent, proSon, courseProperty);
                        String idNumber = quesProperty.getCoursePropertyName() + "-"
                                + quesProperty.getFirstProperty().getId() + "-"
                                + quesProperty.getSecondProperty().getId();
                        quesProperty.setId(idNumber);
                        quesProperties.add(quesProperty);
                    }
                }
            }

            if (isFirstEmpty) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值与课程属性中一级属性不匹配,请检查");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }

            if (isSecondEmpty) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性值与课程属性中二级属性不匹配,请检查");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }
        } else if (StringUtils.isNotBlank(firstProperty) && StringUtils.isBlank(secondProperty)) {
            // 一级属性有值，二级属性为空

            boolean isFirstEmpty = true;
            boolean isSecondEmpty = true;

            // 根据课程查询所有课程属性树
            List<CourseProperty> courseProperties = coursePropertyRepo
                    .findByCourseCodeAndEnable(paper.getCourse().getCode(), true);
            if (courseProperties == null || courseProperties.size() < 1) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,没有设置课程属性结构，请先设置课程属性结构");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }

            for (CourseProperty courseProperty : courseProperties) {
                // 查询一级属性
                Property proParent = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
                		Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);

                // 存在一级属性
                if (proParent != null) {
                    isFirstEmpty = false;
                        // 根据一级属性查询二级属性
                    List<Property> sonProperties = propertyRepo.findByParentIdOrderByNumber(proParent.getId());

                    // 存在二级属性，跳过
                    if (sonProperties != null && sonProperties.size() > 0) {
                        continue;
                    } else {
                        isSecondEmpty = false;
                        // 保存试题属性
                        QuesProperty quesProperty = new QuesProperty(proParent, null, courseProperty);
                        String idNumber = quesProperty.getCoursePropertyName() + "-"
                                + quesProperty.getFirstProperty().getId();
                        quesProperty.setId(idNumber);
                        quesProperties.add(quesProperty);
                    }
                }
            }
            if (isFirstEmpty) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值与课程属性中一级属性不匹配,请检查");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }

            if (isSecondEmpty) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性值与课程属性中二级属性不匹配,请检查");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }
        } else {
            // 一级，二级都为空
        }
    }

    /**
     * 校验小题尾信息是否含有"一节属性"，"二级属性","难度","公开"
     *
     * @param firstProperty
     * @param secondProperty
     * @param difficulty
     * @param publicity
     */
    private void checkAttributeIsFull(String firstProperty, String secondProperty, Double difficulty, Boolean publicity,
            ImportPaperCheck importPaperCheck, int subQuesNum) throws Exception {
        if (firstProperty == null) {
                importPaperCheck
                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性缺失,请检查");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }

        if (secondProperty == null) {
                importPaperCheck
                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性缺失,请检查");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }

        if (difficulty == null) {
                importPaperCheck
                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题难度缺失,请检查");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }

        if (publicity == null) {
                importPaperCheck
                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题公开性缺失,请检查");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
    }

    /**
     * 处理选择题option信息
     *
     * @param question
     */
    public void processSelectOption(Question question) {
        String answer = question.getQuesAnswer();
        if (StringUtils.isNotBlank(answer)) {
            String[] answerArray = answer.split(",");
            for (int i = 0; i < question.getQuesOptions().size(); i++) {
                QuesOption quesOption = question.getQuesOptions().get(i);
                char number = (char) (Integer.parseInt(quesOption.getNumber()) + 64);
                if (ArrayUtils.contains(answerArray, String.valueOf(number))) {
                    quesOption.setIsCorrect((short) 1);
                } else {
                    quesOption.setIsCorrect((short) 0);
                }
            }
        }
    }

    /**
     * 处理套题
     *
     * @param pList
     * @param index
     * @param question
     * @param paperDetailUnit
     * @param importPaperCheck
     * @throws Exception
     */
    public void processNestedQues(List<Object> pList, int index, Question question, PaperDetailUnit paperDetailUnit,
            ImportPaperCheck importPaperCheck, WordprocessingMLPackage wordMLPackage, Paper paper) throws Exception {
        // 题型
        String nestedQuesType = "";
        // 处理套题的头信息
        processNestedQuestionHead(pList, index, 0, question, importPaperCheck, paperDetailUnit);
        // 设置套题题干
        processQuesBody(pList, importPaperCheck.getIndex(), 0, question, importPaperCheck, wordMLPackage, 0);
        // 创建小题集合
        List<Question> subQuesList = new ArrayList<>();
        Question subQues = null;
        int beginNum = importPaperCheck.getIndex();
        int quesTypeNum = 0;
        int subQuesNum = 0;
        boolean hasQuesType = false;
        for (int i = beginNum; i < pList.size(); i++) {
            P pSubQues = (P) pList.get(i);
            String tmpText = DocxProcessUtil.getPText(pSubQues);
            if (StringUtils.isEmpty(tmpText)) {
                continue;
            }
            if (tmpText.startsWith(ImportPaperMsg.subQuestionType_word)) {
                nestedQuesType = getContent(tmpText, ImportPaperMsg.subQuestionType_word);
                importPaperCheck.setIndex(importPaperCheck.getIndex() + 1);
                quesTypeNum++;
                hasQuesType = true;
            } else if (tmpText.matches("^\\d{1,}\\.[\\s\\S]*")) {
                ++subQuesNum;
                if (!hasQuesType) {
                    if (importPaperCheck.getNestedHeadNumber() == 0) {
                        importPaperCheck.setErrorInfo(
                                "[大题名称]：" + importPaperCheck.getQuesName() + "第" + subQuesNum + "题中,缺失[小题型]，请检查套题");
                    } else {
                        importPaperCheck.setErrorInfo("[大题名称]：" + importPaperCheck.getQuesName() + "第"
                                + importPaperCheck.getNestedHeadNumber() + "个套题中,第" + subQuesNum + "题缺失[小题型]，请检查套题");
                    }
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                if (StringUtils.isBlank(nestedQuesType) || getQuesStructType(nestedQuesType) == null) {
                    importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                            + importPaperCheck.getNestedHeadNumber() + "个题" + subQuesNum + "中,"
                            + ImportPaperMsg.errMsg_02 + "(" + ImportPaperMsg.errMsg_09 + ")");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                subQues = new Question();
                subQues.setId(new ObjectId().toString());
                subQues.setQuestionType(getQuesStructType(nestedQuesType));
                if (StringUtils.isNumeric(importPaperCheck.getQuesScore())) {
                    subQues.setScore(Double.parseDouble(importPaperCheck.getQuesScore()));
                }
                // 处理客观题
                if (nestedQuesType.equals(ImportPaperMsg.singleSelection)
                        || nestedQuesType.equals(ImportPaperMsg.multipleSelection)) {
                    // 处理题干
                    processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, subQues, importPaperCheck,
                            wordMLPackage, subQuesNum);
                    // 处理选项
                    processQuesOption(pList, importPaperCheck.getIndex(), subQuesNum, subQues, importPaperCheck,
                            wordMLPackage, subQuesNum);
                    // 处理尾信息
                    processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, subQues, paperDetailUnit,
                            importPaperCheck, wordMLPackage, true, paper, subQuesNum);
                    // 处理选择题的option--chenken 20170426
                    processSelectOption(subQues);
                } else {
                    // 处理其他题型
                    processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, subQues, importPaperCheck,
                            wordMLPackage, subQuesNum);
                    processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, subQues, paperDetailUnit,
                            importPaperCheck, wordMLPackage, true, paper, subQuesNum);
                    // 填空题空格校验
                    if (QuesStructType.FILL_BLANK_QUESTION.getName().equals(subQues.getQuestionType().getName())) {
                        processFill(subQues, paperDetailUnit, importPaperCheck, subQuesNum, subQuesNum);
                    }
                }
                subQuesList.add(subQues);
                i = importPaperCheck.getIndex() - 1;
                nestedQuesType = "";
                hasQuesType = false;
            } else if (subQuesNum != quesTypeNum) {
                if (StringUtils.isEmpty(nestedQuesType)) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, quesTypeNum) + ImportPaperMsg.errMsg_02);
                } else {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, quesTypeNum) + ImportPaperMsg.errMsg_03);
                }
                throw new PaperException(importPaperCheck.getErrorInfo());
            } else if (isHeader(tmpText)) {
                break;
            }
            if (!StringUtils.isEmpty(importPaperCheck.getErrorInfo())) {
                return;
            }
        }
        // 判断标签是否丢失
        if (importPaperCheck.getNestedQuesNumber() == 0 || importPaperCheck.getNestedHeadNumber() == 0) {
            importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "中,套题可能缺失【套题】和【套题数量】标签，请检查套题");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
        // 判断子题数量是否一样
        if (importPaperCheck.getNestedQuesNumber() != subQuesList.size()) {
            importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第" + importPaperCheck.getNestedHeadNumber()
                    + "个套题" + "中,套题数量与子题数量不一样，请检查套题");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
        question.setSubQuestions(subQuesList);
        // 设置套题主题干的 难度，公开度，一级属性，二级属性
        setAttributes(question, subQuesList);
        // 计算套题总分
        double totalScore = 0;
        List<Double> scoreList = new ArrayList<>();
        for (Question child : subQuesList) {
            Double score = child.getScore();
            if (score != null) {
                totalScore += score;
                scoreList.add(score);
            } else {
                scoreList.add(0d);
            }
        }
        question.setScore(totalScore);
        paperDetailUnit.setSubScoreList(scoreList);
    }

    // 设置套题主题干的 难度，公开度，一级属性，二级属性
    private void setAttributes(Question question, List<Question> subQuesList) {
        if (subQuesList != null && subQuesList.size() > 0) {
            Double totalDouble = 0d;
            Double sum = 0.0;
            Boolean publicity = false;
            List<QuesProperty> subQuesProperties = new ArrayList<>();
            for (Question subQuestion : subQuesList) {
                // 设置一级属性，二级属性
                List<QuesProperty> quesProperties = subQuestion.getQuesProperties();
                if (quesProperties != null && quesProperties.size() > 0) {
                    for (QuesProperty quesProperty : quesProperties) {
                        subQuesProperties.add(quesProperty);
                    }
                }
                // 设置公开度
                if (subQuestion.getPublicity()) {
                    publicity = true;
                }
                sum = subQuestion.getDifficultyDegree() * subQuestion.getScore() + sum;
                totalDouble = subQuestion.getScore() + totalDouble;
            }

            BigDecimal b = BigDecimal.valueOf(sum / totalDouble);
            Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            // 给属性去重
            Set<QuesProperty> ts = new HashSet<>();
            ts.addAll(subQuesProperties);
            subQuesProperties.clear();
            subQuesProperties.addAll(ts);
            question.setDifficultyDegree(difficulty);
            question.setPublicity(publicity);
            question.setQuesProperties(subQuesProperties);
            question.setDifficulty(setDiff(difficulty));
        }
    }

    /**
     * 获取试题类型
     *
     * @param quesType
     * @return
     */
    public static QuestionType getQuesType(String quesType) {
        return getQuesType(quesType, "");
    }

    /**
     * 获取试题类型
     *
     * @param quesType
     * @param quesName
     * @return
     */
    public static QuestionType getQuesType(String quesType, String quesName) {
        QuesStructType quesStructType = CommonUtils.getEnum(QuesStructType.class, quesType);
        return new QuestionType(quesStructType, quesName);
    }

    /**
     * 获取试题结构类型
     *
     * @param quesType
     * @return
     */
    public static QuesStructType getQuesStructType(String quesType) {
        return CommonUtils.getEnum(QuesStructType.class, quesType);
    }

    /**
     * 获取当前试题大题小题号
     *
     * @param quesType
     * @param subQuesNum
     * @return
     */
    public static String getQuesNumInfo(ImportPaperCheck importPaperCheck, int subQuesNum) {
        if (importPaperCheck.getNestedHeadNumber() == 0) {
        	return "[大题名称]：" + importPaperCheck.getQuesName() + "第" + subQuesNum + "小题";
        } else {
        	return "[大题名称]：" + importPaperCheck.getQuesName() + "第"
                    + importPaperCheck.getNestedHeadNumber() + "小题,第" + subQuesNum
                    + "子题";
        }
    }

    /**
     * 判断是否为套题
     *
     * @param importPaperCheck
     * @return
     */
    public static boolean isNested(ImportPaperCheck importPaperCheck) {
        if (importPaperCheck.getQuesType().equals(ImportPaperMsg.nestedQuestion_word)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取课程代码
     *
     * @param importPaperCheck
     * @return
     */
    public static String getCourseNo(ImportPaperCheck importPaperCheck) {
        String[] courseInfo = importPaperCheck.getQuesGroup().split("_");
        if (courseInfo.length == 2) {
            return courseInfo[1];
        } else {
            return "";
        }
    }

    /**
     * 获取课程名称
     *
     * @param importPaperCheck
     * @return
     */
    public static String getCourseName(ImportPaperCheck importPaperCheck) {
        String[] courseInfo = importPaperCheck.getQuesGroup().split("_");
        if (courseInfo.length == 2) {
            return courseInfo[0];
        } else {
            return "";
        }
    }

    /**
     * 构造一张空白的导入类型试卷
     *
     * @param courseNo
     * @param paperName
     * @return
     */
    public Map<String, Object> saveBlankPaper(String courseNo, String paperName, User user) {
        Map<String, Object> returnMap = new HashMap<>();
        Paper paper = new Paper();

        boolean existName = paperService.checkPaperName(paperName, user.getRootOrgId().toString());
        if (existName) {
            returnMap.put("msg", "试卷名称重复，请重新命名！");
            return returnMap;
        }

        paper.setCourseNo(courseNo);
        initPaper(paper, paperName, user);
        PaperDetail pd = new PaperDetail();
        pd.setId(new ObjectId().toString());
        pd.setNumber(1);
        pd.setPaper(paper);
        pd.setCreator(user.getDisplayName());
        pd.setCreateTime(CommonUtils.getCurDateTime());
        paper = paperRepo.save(paper);
        paperDetailRepo.save(pd);
        returnMap.put("paper", paper);
        returnMap.put("msg", "success");
        return returnMap;
    }

    /**
     * 校验试卷总分是否与设定一致
     *
     * @param paper
     * @param paperDetailUnits
     * @param importPaperCheck
     * @throws PaperException
     */
    private void checkTotalScore(Paper paper, List<PaperDetailUnit> paperDetailUnits, ImportPaperCheck importPaperCheck)
            throws PaperException {
        double totalScore = 0;
        for (PaperDetailUnit unit : paperDetailUnits) {
            if (unit.getScore() != null) {
                totalScore += unit.getScore();
            }
        }
        if (paper.getTotalScore() != null && paper.getTotalScore() != totalScore) {
            importPaperCheck.setErrorInfo("试卷总分(" + totalScore + ")与设置总分(" + paper.getTotalScore() + ")不符");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
    }

    /**
     * 校验大题下小题数量是否与标识数量一致
     *
     * @throws PaperException
     */
    private void checkUnitNum(List<PaperDetailUnit> paperDetailUnits, ImportPaperCheck importPaperCheck)
            throws PaperException {
        Map<PaperDetail, Integer> unitNumMap = new HashMap<>();
        Collections.sort(paperDetailUnits);

        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
            PaperDetail key = paperDetailUnit.getPaperDetail();
            if (unitNumMap.containsKey(key)) {
                int value = unitNumMap.get(key);
                /*
                 * if (paperDetailUnit.getQuestionType() ==
                 * QuesStructType.NESTED_ANSWER_QUESTION) { unitNumMap.put(key,
                 * value + 1); } else { unitNumMap.put(key, value + 1); }
                 */
                unitNumMap.put(key, value + 1);
            } else {
                /*
                 * if (paperDetailUnit.getQuestionType() ==
                 * QuesStructType.NESTED_ANSWER_QUESTION) { unitNumMap.put(key,
                 * 1); } else { unitNumMap.put(key, 1); }
                 */
                unitNumMap.put(key, 1);
            }
        }

        for (Map.Entry<PaperDetail, Integer> entry : unitNumMap.entrySet()) {
            PaperDetail paperDetail = entry.getKey();
            if (paperDetail.getUnitCount().intValue() != entry.getValue().intValue()) {
                importPaperCheck.setErrorInfo("大题:" + paperDetail.getName() + "标识小题数量(" + paperDetail.getUnitCount()
                        + ")与实际小题数量(" + entry.getValue() + ")不符。" + "(注:若检查小题数量无问题,请检查word中题目的格式是否正确)");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }
        }
    }

    /**
     * 校验选择题答案 1.判断答案格式，选择题答案必须以","分隔,且都为字母 2.判断答案在选项中是否不存在,例如选项为A,B,C,D，答案为E
     *
     * @param question
     * @param answerWord
     * @param importPaperCheck
     * @throws PaperException
     */
    private void checkSelectAnswer(Question question, String answerWord, ImportPaperCheck importPaperCheck,
            int subQuesNum) throws PaperException {
        if (!StringUtils.isBlank(answerWord)) {
            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION&&answerWord.contains(",")) {
                importPaperCheck.setErrorInfo(
                        getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + answerWord);
                throw new PaperException(importPaperCheck.getErrorInfo());
            }
            String[] pAnswerArray = answerWord.split(",");
            List<QuesOption> options = question.getQuesOptions();
            List<String> optionNumList = new ArrayList<>();
            for (QuesOption quesOption : options) {
                Integer numInteger = Integer.parseInt(quesOption.getNumber());
                char word = (char) (numInteger + 64);
                optionNumList.add(word + "");
            }
            for (String answer : pAnswerArray) {
                answer = answer.trim();
                String pattern = "[A-Z]|[a-z]";
                if (!Pattern.matches(pattern, answer)) {
                    importPaperCheck.setErrorInfo(
                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + answerWord);
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
                if (!optionNumList.contains(answer)) {
                    importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,选项为:"
                            + optionNumList.toString() + ",答案为:" + answerWord + "。(注:若检查选项无问题,请检查word中题目的格式是否正确)");
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
            }
        }
    }

    /**
     * 校验判断题答案
     *
     * @param tmpText
     * @param importPaperCheck
     * @param subQuesNum
     * @throws Exception
     */
    private void checkBoolAnswer(String tmpText, ImportPaperCheck importPaperCheck, int subQuesNum) throws Exception {
        if (!StringUtils.isBlank(tmpText)) {
            tmpText = tmpText.trim();
            if (tmpText.equals("正确") || tmpText.equals("错误")) {
                return;
            } else {
                importPaperCheck.setErrorInfo(
                        getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,应为:正确或错误");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }
        }
    }

    /**
     * 校验试题难度
     */
    private Double checkDifficulty(String tmpText, ImportPaperCheck importPaperCheck, int subQuesNum, Question question)
            throws Exception {
        Double tempDifficulty;
        if (StringUtils.isBlank(tmpText)) {
            // 如果为空，默认难度0.5
            tempDifficulty = 0.5;
            question.setDifficulty("中");
        } else if (!isInteger(tmpText) || (Double.parseDouble(tmpText) < 1) || (Double.parseDouble(tmpText) > 10)) {
            // 如果不是整数，或者在1到10之间，就报错
            importPaperCheck
                    .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,试题难度只能是1到10之间整数");
            throw new PaperException(importPaperCheck.getErrorInfo());
        } else {
            tempDifficulty = Double.parseDouble(tmpText) / 10;
            question.setDifficulty(setDiff(tempDifficulty));
        }

        return tempDifficulty;
    }

    private String setDiff(Double difficulty) {
        if (difficulty > 0 && difficulty < 0.4) {
            return "难";
        } else if (difficulty > 0.3 && difficulty < 0.8) {
            return "中";
        } else {
            return "易";
        }
    }

    /**
     * 校验试题公开度
     */
    private Boolean checkPublicity(String tmpText, ImportPaperCheck importPaperCheck, int subQuesNum) throws Exception {
        Boolean publicity;
        if (StringUtils.isBlank(tmpText)) {
            // 如果为空，默认是公开
            publicity = true;
        } else if (!tmpText.equals("公开") && !tmpText.equals("非公开")) {
            // 如果不是公开和非公开，就报错
            importPaperCheck
                    .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,试题公开度只能是公开和非公开");
            throw new PaperException(importPaperCheck.getErrorInfo());
        } else {
            if (tmpText.equals("非公开")) {
                publicity = false;
            } else {
                publicity = true;
            }
        }
        return publicity;
    }

    /**
     * 校验客观题答案完整性 客观题答案要么全有，要么全没有
     *
     * @throws PaperException
     */
    private void checkAnswerISfull(List<PaperDetailUnit> paperDetailUnits, ImportPaperCheck importPaperCheck)
            throws PaperException {
        Map<Question, PaperDetailUnit> map = new HashMap<>();
        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
            Question question = paperDetailUnit.getQuestion();
            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                    || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
                    || question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
                map.put(question, paperDetailUnit);
            }
            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                List<Question> subQuestions = question.getSubQuestions();
                for (Question subQuestion : subQuestions) {
                    if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                            || subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
                            || subQuestion.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
                        map.put(subQuestion, paperDetailUnit);
                    }
                }
            }
        }
        boolean isNull = false;
        boolean isNotNull = false;
        PaperDetailUnit paperDetailUnit = new PaperDetailUnit();
        Question subQuestion = new Question();
        for (Question question : map.keySet()) {
            PaperDetailUnit pdu = map.get(question);
            if (StringUtils.isBlank(question.getQuesAnswer())) {
                isNull = true;
                if (pdu.getQuestion().getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                    subQuestion = question;
                }
                paperDetailUnit = pdu;
                break;
            }
        }
        for (Question question : map.keySet()) {
            // PaperDetailUnit pdu = map.get(question);
            if (StringUtils.isNotBlank(question.getQuesAnswer())) {
                isNotNull = true;
                break;
            }
        }
        // 不满足(客观题答案要么全有，要么全没有)条件
        if (isNull && isNotNull) {
            if (paperDetailUnit.getQuestion().getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                int number = 0;
                for (int i = 0; i < paperDetailUnit.getQuestion().getSubQuestions().size(); i++) {
                    if (paperDetailUnit.getQuestion().getSubQuestions().get(i).getId().equals(subQuestion.getId())) {
                        number = i + 1;
                    }
                }
                importPaperCheck.setErrorInfo("第" + paperDetailUnit.getNumber() + "大题套题中" + number + "小题答案缺失,请检查");
                throw new PaperException(importPaperCheck.getErrorInfo());
            }
            importPaperCheck.setErrorInfo("第" + paperDetailUnit.getNumber() + "小题答案缺失,请检查");
            throw new PaperException(importPaperCheck.getErrorInfo());
        }
    }

    /**
     * 替换字符串中的属性头得到属性值
     *
     * @param pText
     * @param replaceContent
     * @return
     */
    private static String getContent(String pText, String replaceContent) {
        String word = replaceContent.replace("[", "").replace("]", "");
        return pText.replaceAll("\\[" + word + "\\]", "").replaceAll("[:|：]", "").trim();
    }

    /**
     * 克隆试卷
     *
     * @param paperId
     * @param paperName
     * @param courseNo
     * @param user
     * @return
     */
    public Map<String, Object> clonePaper(String paperId, String paperName, String courseNo, User user) {
        Map<String, Object> map = new HashMap<>();
        boolean existName = paperService.checkPaperName(paperName, user.getRootOrgId().toString());
        if (existName) {
            map.put("msg", "试卷名称重复，请重新命名！");
            return map;
        }

        // 根据paperId查询试卷
        Paper oldPaper = Model.of(paperRepo.findById(paperId));
        if (oldPaper == null) {
            map.put("msg", "克隆试卷不存在！");
            return map;
        }

        // 新课程
        Course course = courseService.getCourse(user.getRootOrgId(), courseNo);

        // 克隆原试卷所有属性
        Paper newPaper = cloneOldPaper(oldPaper, paperName, course, user);

        // 克隆所有大题和小题
        clonePaperDetails(newPaper, oldPaper, paperName, user, course);
        map.put("msg", "success");
        return map;
    }

    // 克隆所有大题和小题
    private void clonePaperDetails(Paper newPaper, Paper oldPaper, String paperName, User user, Course course) {
        List<PaperDetail> oldPaperDetails = paperDetailRepo.findByPaperOrderByNumber(oldPaper);
        if (oldPaperDetails != null && oldPaperDetails.size() > 0) {
            for (PaperDetail oldPaperDetail : oldPaperDetails) {
                PaperDetail paperDetail = new PaperDetail();
                // 给大题对象属性赋值
                paperDetail.setPaper(newPaper);
                paperDetail.setNumber(oldPaperDetail.getNumber());
                paperDetail.setName(oldPaperDetail.getName());
                paperDetail.setTitle(oldPaperDetail.getTitle());
                paperDetail.setScore(oldPaperDetail.getScore());
                paperDetail.setUnitCount(oldPaperDetail.getUnitCount());
                paperDetail.setCreator(user.getDisplayName());
                paperDetail.setCreateTime(CommonUtils.getCurDateTime());
                paperDetail = paperDetailRepo.save(paperDetail);
                // 查询大题下面所有小题
                List<PaperDetailUnit> oldPaperDetailUnits = paperDetailUnitRepo
                        .findByPaperDetailOrderByNumber(oldPaperDetail);
                if (oldPaperDetailUnits != null && oldPaperDetailUnits.size() > 0) {
                    for (PaperDetailUnit oldPaperDetailUnit : oldPaperDetailUnits) {
                        PaperDetailUnit paperDetailUnit = new PaperDetailUnit();
                        paperDetailUnit.setPaper(newPaper);
                        paperDetailUnit.setNumber(oldPaperDetailUnit.getNumber());
                        paperDetailUnit.setScore(oldPaperDetailUnit.getScore());
                        paperDetailUnit.setSubScoreList(oldPaperDetailUnit.getSubScoreList());
                        paperDetailUnit.setPaperDetail(paperDetail);
                        paperDetailUnit.setQuestionType(oldPaperDetailUnit.getQuestionType());
                        copyQuestion(paperDetailUnit, oldPaperDetailUnit, course, user);
                        paperDetailUnit.setOptionOrder(oldPaperDetailUnit.getOptionOrder());
                        paperDetailUnit.setCreator(user.getDisplayName());
                        paperDetailUnit.setCreateTime(CommonUtils.getCurDateTime());
                        paperDetailUnit.setPaperType(oldPaperDetailUnit.getPaperType());
                        paperDetailUnitRepo.save(paperDetailUnit);
                    }
                    quesTypeNameService.saveCloneQuesTypeName(oldPaperDetailUnits, newPaper.getCourseNo());
                }
            }
        }
    }

    private void copyQuestion(PaperDetailUnit paperDetailUnit, PaperDetailUnit oldPaperDetailUnit, Course course,
            User user) {
        Question oldQuestion = oldPaperDetailUnit.getQuestion();
        Question newQuestion = BeanCopierUtil.copyProperties(oldQuestion, Question.class);
//        QuestionPkgPath oldQuesPkgPath = quesPkgPathRepo.findFirstById(oldQuestion.getQuesPkgPathId());
//        QuestionPkgPath newQuestionPkgPath = BeanCopierUtil.copyProperties(oldQuesPkgPath, QuestionPkgPath.class);
//        newQuestionPkgPath.setId(null);
//        newQuestionPkgPath = quesPkgPathRepo.save(newQuestionPkgPath);
//        newQuestion.setQuesPkgPathId(newQuestionPkgPath.getId());
        newQuestion.setCourse(course);
        newQuestion.setId(null);
        newQuestion.setQuesProperties(null);
        newQuestion.setPropertyGroup(null);
        newQuestion = quesRepo.save(newQuestion);
        // 复制音频
        if (oldQuestion.getHasAudio() != null && oldQuestion.getHasAudio()) {
//            try {
                // 查询旧音频
                List<QuestionAudio> oldQuestionAudios = questionAudioService
                        .findQuestionAudiosByQuestionId(oldQuestion.getId());
                // 复制文件下载路径
                String copyAudioPath = TEMP_FILE_EXP + File.separator + user.getDisplayName() + "_copyAudioPath";
                // 新建文件夹
                File copyAudioDir = new File(copyAudioPath);
                if (!copyAudioDir.exists()) {
                    copyAudioDir.mkdirs();
                }
                for (QuestionAudio oldAudio : oldQuestionAudios) {
                    // 生成随机数，防止文件重名
                    int randomNumber = random.nextInt(1000);
                    // 定义文件下载名称，下载音频文件
                    String newAudioFileName = newQuestion.getId() + "_" + randomNumber + "_" + oldAudio.getFileName();
//                    UpYun upyun = new UpYun(sysProperty.getBucketName(), sysProperty.getUserName(),
//                            sysProperty.getPassword());
                    // 下载音频
                    File audioFile = new File(copyAudioPath + File.separator + newAudioFileName);
                    try {
//                        upyun.readFile(oldAudio.getFileUrl(), audioFile);
                      //通用存储
                        FileStorageUtil.saveUrlAs(FileStorageUtil.realPath(oldAudio.getFileUrl()), audioFile);
                    } catch (Exception e) {
                        log.error(e.getMessage(), e);
                        throw new RuntimeException("下载音频失败");
                    }
                    // 重新上传新的音频文件
//                    upyun.writeFile(sysProperty.getRadioUploadPath() + newAudioFileName, audioFile, true);
                  //通用存储
            		FileStoragePathEnvInfo env=new FileStoragePathEnvInfo();
            		env.setRelativePath(sysProperty.getRadioUploadPath() + newAudioFileName);
            		YunPathInfo pi=FileStorageUtil.saveFile("audioFile", env, audioFile,null);
            		
                    IoUtils.removeFile(audioFile);

                    // 拷贝旧对象
                    QuestionAudio newAudio = new QuestionAudio(newQuestion.getId(), oldAudio.getFileName(),pi.getRelativePath());
                    newAudio.setCreateTime(new Date());
                    newAudio.setCreateUser(user.getDisplayName());
                    questionAudioService.saveQuestionAudio(newAudio, user);
                }
                // 删除文件夹
                IoUtils.removeFile(copyAudioDir);

                // 查询新音频
                List<QuestionAudio> newQuestionAudios = questionAudioService
                        .findQuestionAudiosByQuestionId(newQuestion.getId());
                Map<String, String> newMap = new HashMap<>();
                for (QuestionAudio audio : newQuestionAudios) {
                    newMap.put(audio.getFileName(), audio.getId());
                }
                // 在题干html片段中替换音频存储id
                String body = newQuestion.getQuesBody();
                List<String> ids = CommonUtils.getTagANames(body);
                List<String> names = CommonUtils.getTagANames2(body);
                Map<String, String> oldBodyMap = new HashMap<>();
                for (int i = 0; i < ids.size(); i++) {
                    oldBodyMap.put(names.get(i), ids.get(i));
                }
                for (String key : oldBodyMap.keySet()) {
                    body = body.replace(oldBodyMap.get(key), newMap.get(key));
                }
                newQuestion.setQuesBody(body);
                // 替换选项中的音频存储id
                if (newQuestion.getQuesOptions() != null && newQuestion.getQuesOptions().size() > 0) {
                    for (QuesOption option : newQuestion.getQuesOptions()) {
                        String newOptionBody = option.getOptionBody();
                        List<String> optionIds = CommonUtils.getTagANames(newOptionBody);
                        List<String> optionNames = CommonUtils.getTagANames2(newOptionBody);
                        Map<String, String> oldOptionMap = new HashMap<>();
                        for (int i = 0; i < optionIds.size(); i++) {
                            oldOptionMap.put(optionNames.get(i), optionIds.get(i));
                        }
                        for (String key : oldOptionMap.keySet()) {
                            newOptionBody = newOptionBody.replace(oldOptionMap.get(key), newMap.get(key));
                        }
                        option.setOptionBody(newOptionBody);
                    }
                }
                newQuestion = quesRepo.save(newQuestion);
//            } catch (IOException e) {
//                log.error(e.getMessage(), e);
//            }
        }
        paperDetailUnit.setQuestion(newQuestion);
    }

    // 克隆原试卷所有属性
    private Paper cloneOldPaper(Paper oldPaper, String paperName, Course course, User user) {
        Paper newPaper = BeanCopierUtil.copyProperties(oldPaper, Paper.class);
        newPaper.setName(paperName);
        newPaper.setCreateTime(CommonUtils.getCurDateTime());
        newPaper.setLastModifyName(user.getDisplayName());
        newPaper.setOrgId(user.getRootOrgId().toString());
        newPaper.setCourse(course);
        newPaper.setCourseNo(course.getCode());
        newPaper.setId(null);
        return paperRepo.save(newPaper);
    }

    public static boolean isInteger(String str) {
        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
        return pattern.matcher(str).matches();
    }

    /**
     * 判断填空题题干和答案空格数量是否一致
     *
     * @throws PaperException
     */
    public void processFill(Question question, PaperDetailUnit paperDetailUnit, ImportPaperCheck importPaperCheck,
            int subQuesNum, int errorQuesNum) throws PaperException {
    	if(!question.getQuesBody().contains("###")) {
    		if (importPaperCheck.getNestedHeadNumber() == 0) {
                importPaperCheck.setErrorInfo(
                        "[大题名称]：" + importPaperCheck.getQuesName() + "第" + errorQuesNum + "题中,题干中没有空格");
            } else {
                importPaperCheck.setErrorInfo("[大题名称]：" + importPaperCheck.getQuesName() + "第"
                        + importPaperCheck.getNestedHeadNumber() + "个套题中,第" + errorQuesNum
                        + "题中,题干中没有空格");
            }
            throw new PaperException(importPaperCheck.getErrorInfo());
    	}
        // 按3个#号截取题干
        String[] quesBody = question.getQuesBody().split("###");
        String[] quesAnwser = question.getQuesAnswer().split("##");
        // 如果有答案，进行判断
        if (quesAnwser.length > 0) {
            String str = quesAnwser[0].replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
                    .replaceAll("</span>", "").replace("&nbsp;", "");
            if (quesAnwser.length == 1 && !StringUtils.isBlank(str) || quesAnwser.length > 1) {
                // 判断题干空格和答案空格是否相等
                if ((quesBody.length - 1) != quesAnwser.length) {
                    if (importPaperCheck.getNestedHeadNumber() == 0) {
                        importPaperCheck.setErrorInfo(
                                "[大题名称]：" + importPaperCheck.getQuesName() + "第" + errorQuesNum + "题中,题干与答案的空格数量不一样");
                    } else {
                        importPaperCheck.setErrorInfo("[大题名称]：" + importPaperCheck.getQuesName() + "第"
                                + importPaperCheck.getNestedHeadNumber() + "个套题中,第" + errorQuesNum
                                + "题中,题干与答案的空格数量不一样");
                    }
                    throw new PaperException(importPaperCheck.getErrorInfo());
                }
            }
        }
    }
}
