package cn.com.qmth.stmms.admin.exam;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import cn.com.qmth.stmms.admin.thread.MarkGroupDeleteThread;
import cn.com.qmth.stmms.biz.config.service.impl.SystemCache;
import cn.com.qmth.stmms.biz.exam.model.Exam;
import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
import cn.com.qmth.stmms.biz.exam.model.SelectiveGroup;
import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
import cn.com.qmth.stmms.biz.exam.service.ExamService;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
import cn.com.qmth.stmms.biz.exam.service.InspectHistoryService;
import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
import cn.com.qmth.stmms.biz.exam.service.MarkerService;
import cn.com.qmth.stmms.biz.exam.service.SelectiveGroupService;
import cn.com.qmth.stmms.biz.file.service.FileService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.mark.model.MarkConfigItem;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.biz.report.service.ReportSubjectService;
import cn.com.qmth.stmms.biz.school.model.School;
import cn.com.qmth.stmms.biz.school.service.SchoolService;
import cn.com.qmth.stmms.common.annotation.Logging;
import cn.com.qmth.stmms.common.annotation.RoleRequire;
import cn.com.qmth.stmms.common.domain.WebUser;
import cn.com.qmth.stmms.common.enums.ExamType;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.LogType;
import cn.com.qmth.stmms.common.enums.MarkMode;
import cn.com.qmth.stmms.common.enums.MarkStatus;
import cn.com.qmth.stmms.common.enums.Role;
import cn.com.qmth.stmms.common.enums.ScorePolicy;
import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
import cn.com.qmth.stmms.common.enums.ThirdPolicy;
import cn.com.qmth.stmms.common.utils.RequestUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Controller("markGroupController")
@RequestMapping("/admin/exam/group")
public class MarkGroupController extends BaseExamController {

    protected static Logger log = LoggerFactory.getLogger(MarkGroupController.class);

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private ExamQuestionService questionService;

    @Autowired
    private MarkGroupService groupService;

    @Autowired
    private MarkerService markerService;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private MarkService markService;

    @Autowired
    private LockService lockService;

    @Autowired
    private ExamService examService;

    @Autowired
    private FileService fileService;

    @Autowired
    private SelectiveGroupService selectiveGroupService;

    @Autowired
    private ReportSubjectService reportSubjectService;

    @Autowired
    private InspectHistoryService inspectHistoryService;

    @Autowired
    private SchoolService schoolService;

    @Qualifier("task-executor")
    @Autowired
    private AsyncTaskExecutor taskExecutor;

    @Value("${mark.group.delete}")
    private String markDeleteCode;

    @Autowired
    private SystemCache systemCache;

    @Logging(menu = "大题管理查询", type = LogType.QUERY)
    @RequestMapping
    public String list(HttpServletRequest request, Model model, @RequestParam(required = false) String subjectCode) {
        WebUser wu = RequestUtils.getWebUser(request);
        int examId = getSessionExamId(request);
        List<ExamSubject> subjectList = getExamSubject(examId, wu);
        ExamSubject subject = null;
        if (StringUtils.isNotBlank(subjectCode)) {
            subject = subjectService.find(examId, subjectCode);
        } else if (!subjectList.isEmpty()) {
            subject = subjectList.get(0);
        }
        if (subject == null) {
            return "modules/exam/groupList";
        }
        List<MarkGroup> list = groupService.findByExamAndSubject(examId, subject.getCode());
        for (MarkGroup group : list) {
            List<ExamQuestion> qList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
                    subject.getCode(), false, group.getNumber());
            group.setQuestionList(qList);
            group.setMarkerCount(
                    markerService.countByExamAndSubjectAndGroup(examId, subject.getCode(), group.getNumber()));
            group.setCurrentCount(markService.applyCount(group));
            int percent = group.getLibraryCount() > 0
                    ? (int) (group.getMarkedCount() * 100.00 / group.getLibraryCount())
                    : 0;
            if (group.getMarkedCount() > 0 && percent == 0) {
                percent = 1;
            } else if (group.getLeftCount() > 0 && percent == 100) {
                percent = 99;
            }
            group.setPercent(percent);
            group.setDeleting(lockService.isLocked(LockType.GROUP_DELETE, group.getExamId(), group.getSubjectCode(),
                    group.getNumber()));
            if (!qList.isEmpty() && group.isSelective()) {
                SelectiveGroup selectiveGroup = selectiveGroupService.findOne(examId, group.getSubjectCode(),
                        qList.get(0).getMainNumber());
                if (selectiveGroup != null) {
                    group.setSelectiveIndex(selectiveGroup.getSelectiveIndex());
                }
            }
        }
        model.addAttribute("resultList", list);
        model.addAttribute("subject", subject);
        model.addAttribute("subjectList", subjectList);
        Exam exam = examService.findById(examId);
        model.addAttribute("examType", exam.getType());
        return "modules/exam/groupList";
    }

    @Logging(menu = "大题管理查询(评卷员绑定)", type = LogType.QUERY)
    @RequestMapping("/markerGroup")
    public String listForMarker(HttpServletRequest request, Model model,
            @RequestParam(required = false) String subjectCode, @RequestParam(required = false) String userIds) {
        WebUser wu = RequestUtils.getWebUser(request);
        int examId = getSessionExamId(request);
        List<ExamSubject> subjectList = getExamSubject(examId, wu);
        ExamSubject subject = null;
        if (StringUtils.isNotBlank(subjectCode)) {
            subject = subjectService.find(examId, subjectCode);
        } else if (!subjectList.isEmpty()) {
            subject = subjectList.get(0);
        }
        if (subject == null) {
            return "modules/exam/markerGroup";
        }
        List<MarkGroup> list = groupService.findByExamAndSubject(examId, subject.getCode());
        for (MarkGroup group : list) {
            List<ExamQuestion> qList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
                    subject.getCode(), false, group.getNumber());
            group.setQuestionList(qList);
            group.setMarkerCount(
                    markerService.countByExamAndSubjectAndGroup(examId, subject.getCode(), group.getNumber()));
            group.setCurrentCount(markService.applyCount(group));
            int percent = group.getLibraryCount() > 0
                    ? (int) (group.getMarkedCount() * 100.00 / group.getLibraryCount())
                    : 0;
            if (group.getMarkedCount() > 0 && percent == 0) {
                percent = 1;
            } else if (group.getLeftCount() > 0 && percent == 100) {
                percent = 99;
            }
            group.setPercent(percent);
            group.setDeleting(lockService.isLocked(LockType.GROUP_DELETE, group.getExamId(), group.getSubjectCode(),
                    group.getNumber()));
            if (!qList.isEmpty() && group.isSelective()) {
                SelectiveGroup selectiveGroup = selectiveGroupService.findOne(examId, group.getSubjectCode(),
                        qList.get(0).getMainNumber());
                if (selectiveGroup != null) {
                    group.setSelectiveIndex(selectiveGroup.getSelectiveIndex());
                }
            }
        }
        model.addAttribute("resultList", list);
        model.addAttribute("subject", subject);
        model.addAttribute("userIds", userIds);
        model.addAttribute("subjectList", subjectList);
        Exam exam = examService.findById(examId);
        model.addAttribute("examType", exam.getType());
        return "modules/exam/markerGroup";
    }

    @RequestMapping("/query")
    @ResponseBody
    public JSONArray query(HttpServletRequest request, @RequestParam String subjectCode,
            @RequestParam(required = false) Boolean withDouble, @RequestParam(required = false) MarkStatus status) {
        int examId = getSessionExamId(request);
        JSONArray array = new JSONArray();
        List<MarkGroup> list = withDouble != null && withDouble
                ? groupService.findByExamAndSubjectWithDouble(examId, subjectCode)
                : (status != null ? groupService.findByExamAndSubjectAndStatus(examId, subjectCode, status)
                        : groupService.findByExamAndSubject(examId, subjectCode));
        for (MarkGroup group : list) {
            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
                    false, group.getNumber()));
            JSONObject obj = new JSONObject();
            obj.accumulate("number", group.getNumber());
            obj.accumulate("title", group.getTitle());
            obj.accumulate("score", group.getTotalScore());
            array.add(obj);
        }
        return array;
    }

    @Logging(menu = "大题数量校对", type = LogType.UPDATE)
    @RequestMapping("/check-count")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String ckeckCount(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode) {
        int examId = getSessionExamId(request);
        ExamSubject subject = subjectService.find(examId, subjectCode);
        if (subject == null) {
            return "redirect:/admin/exam/mark";
        }
        markService.updateAllCount(examId, subjectCode);

        redirectAttributes.addAttribute("subjectCode", subjectCode);
        return "redirect:/admin/exam/group";
    }

    @Logging(menu = "大题任务回收", type = LogType.UPDATE)
    @RequestMapping("/release")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String release(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number) {
        int examId = getSessionExamId(request);
        MarkGroup group = groupService.findOne(examId, subjectCode, number);
        if (group == null) {
            return "redirect:/admin/exam/mark";
        }
        try {
            lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
            markService.releaseByGroup(group);
        } finally {
            lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
        }
        redirectAttributes.addAttribute("subjectCode", subjectCode);
        return "redirect:/admin/exam/group";
    }

    @Logging(menu = "大题重置", type = LogType.UPDATE)
    @RequestMapping("/reset")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String reset(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number) {
        int examId = getSessionExamId(request);
        MarkGroup group = groupService.findOne(examId, subjectCode, number);
        if (group == null) {
            return "redirect:/admin/exam/mark";
        }
        try {
            lockService.waitlock(LockType.EXAM_SUBJECT, group.getExamId(), group.getSubjectCode());
            lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
            markService.resetByGroup(group);
        } catch (Exception e) {
            log.error("reset group error", e);
            throw new RuntimeException("重置大题失败", e);
        } finally {
            lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
            lockService.unlock(LockType.EXAM_SUBJECT, group.getExamId(), group.getSubjectCode());
        }

        redirectAttributes.addAttribute("subjectCode", subjectCode);
        return "redirect:/admin/exam/group";
    }

    @Logging(menu = "大题状态修改", type = LogType.QUERY)
    @RequestMapping("/changeStatus")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String changeStatus(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam MarkStatus status) {
        int examId = getSessionExamId(request);
        MarkGroup markGroup = groupService.findOne(examId, subjectCode, number);
        if (markGroup == null) {
            return "redirect:/admin/exam/mark";
        }
        boolean allow = false;
        if (markGroup.getStatus() == MarkStatus.TRIAL && status == MarkStatus.FORMAL) {
            allow = true;
        } else if (markGroup.getStatus() == MarkStatus.FORMAL && status == MarkStatus.FINISH
                && markGroup.getLeftCount() == 0) {
            allow = true;
        } else if (markGroup.getStatus() == MarkStatus.FINISH && status == MarkStatus.FORMAL) {
            allow = true;
        }
        if (!allow) {
            redirectAttributes.addAttribute("message", "不能切换到指定的评卷状态");
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group";
        }
        if (markGroup.getStatus() == MarkStatus.TRIAL && status == MarkStatus.FORMAL) {
            List<MarkGroup> list = groupService.findByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.TRIAL);
            for (MarkGroup group : list) {
                updateStatus(group, status);
            }
            markService.groupTrialToFormal(examId, subjectCode);
        } else {
            updateStatus(markGroup, status);
        }
        redirectAttributes.addAttribute("subjectCode", subjectCode);
        return "redirect:/admin/exam/group";
    }

    private void updateStatus(MarkGroup group, MarkStatus status) {
        try {
            lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
            if (groupService.updateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(), status,
                    group.getStatus()) && status == MarkStatus.FORMAL) {
                // 切换到正评成功后刷新任务数量
                group.setStatus(status);
                markService.updateLibraryCount(group);
                markService.updateMarkedCount(group);
                markerService.logoutByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                        group.getNumber());
            }
        } finally {
            lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
        }
    }

    @RequestMapping("/add")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String add(HttpServletRequest request, Model model, @RequestParam String subjectCode) {
        int examId = getSessionExamId(request);
        ExamSubject subject = subjectService.find(examId, subjectCode);
        if (subject == null) {
            return "redirect:/admin/exam/mark";
        }
        MarkGroup group = new MarkGroup();
        group.setSubjectCode(subjectCode);
        Integer number = groupService.findMaxNumberByExamIdAndSubjectCode(examId, subjectCode);
        group.setNumber(number + 1);
        model.addAttribute("group", group);
        model.addAttribute("subject", subject);
        model.addAttribute("markModeList", MarkMode.values());
        model.addAttribute("scorePolicyList", ScorePolicy.values());
        model.addAttribute("thirdPolicyList", ThirdPolicy.values());
        model.addAttribute("exam", examService.findById(examId));
        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
        List<SelectiveGroup> list = selectiveGroupService.findByExamIdAndSubjectCode(examId, subjectCode);
        Map<Integer, SelectiveGroup> map = new HashMap<Integer, SelectiveGroup>();
        for (SelectiveGroup selectiveGroup : list) {
            map.put(selectiveGroup.getMainNumber(), selectiveGroup);
        }
        for (ExamQuestion examQuestion : questions) {
            if (map.containsKey(examQuestion.getMainNumber())) {
                examQuestion.setSelective(true);
                examQuestion.setSelectiveIndex(map.get(examQuestion.getMainNumber()).getSelectiveIndex());
                examQuestion.setSelectivePart(map.get(examQuestion.getMainNumber()).getSelectivePart());
            }
        }
        model.addAttribute("questionList", questions);
        model.addAttribute("groupCount", groupService.countByExamAndSubject(examId, subjectCode));
        return "modules/exam/groupAdd";
    }

    @RequestMapping("/edit-simple")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String editSimple(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number) {
        int examId = getSessionExamId(request);
        MarkGroup group = groupService.findOne(examId, subjectCode, number);
        if (group != null) {
            String pictureConfig = buildPictureConfig(group);
            group.setPicList(pictureConfig);
            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
                    false, group.getNumber()));
            model.addAttribute("group", group);
            model.addAttribute("questions", questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
                    group.getExamId(), group.getSubjectCode(), false, group.getNumber()));
            model.addAttribute("pictureConfig", pictureConfig);
            model.addAttribute("markModeList", MarkMode.values());
            model.addAttribute("scorePolicyList", ScorePolicy.values());
            model.addAttribute("thirdPolicyList", ThirdPolicy.values());
            model.addAttribute("exam", examService.findById(examId));
            return "modules/exam/groupEditSimple";
        } else {
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group";
        }
    }

    @RequestMapping("/edit-full")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String editFull(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number) {
        int examId = getSessionExamId(request);
        MarkGroup group = groupService.findOne(examId, subjectCode, number);
        if (group != null) {
            group.setPicList(buildPictureConfig(group));
            List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
            List<SelectiveGroup> list = selectiveGroupService.findByExamIdAndSubjectCode(examId, subjectCode);
            Map<Integer, SelectiveGroup> map = new HashMap<Integer, SelectiveGroup>();
            for (SelectiveGroup selectiveGroup : list) {
                map.put(selectiveGroup.getMainNumber(), selectiveGroup);
            }
            for (ExamQuestion examQuestion : questions) {
                if (map.containsKey(examQuestion.getMainNumber())) {
                    examQuestion.setSelective(true);
                    examQuestion.setSelectiveIndex(map.get(examQuestion.getMainNumber()).getSelectiveIndex());
                    examQuestion.setSelectivePart(map.get(examQuestion.getMainNumber()).getSelectivePart());
                }
            }
            int percent = group.getLibraryCount() > 0
                    ? (int) (group.getMarkedCount() * 100.00 / group.getLibraryCount())
                    : 0;
            if (group.getMarkedCount() > 0 && percent == 0) {
                percent = 1;
            } else if (group.getLeftCount() > 0 && percent == 100) {
                percent = 99;
            }
            group.setPercent(percent);
            model.addAttribute("group", group);
            model.addAttribute("questionList", questions);
            model.addAttribute("pictureConfig", group.getPicList());
            model.addAttribute("markModeList", MarkMode.values());
            model.addAttribute("scorePolicyList", ScorePolicy.values());
            model.addAttribute("thirdPolicyList", ThirdPolicy.values());
            model.addAttribute("subject", subjectService.find(examId, subjectCode));
            model.addAttribute("exam", examService.findById(examId));
            Exam exam = examService.findById(examId);
            School school = schoolService.findById(exam.getSchoolId());
            model.addAttribute("deleteCheck", systemCache.isGroupDeleteWarn() ? true : school.isGroupDeleteCheck());
            return "modules/exam/groupEditFull";
        } else {
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group";
        }
    }

    @Logging(menu = "删除大题", type = LogType.DELETE)
    @RequestMapping("/delete")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String delete(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number,
            @RequestParam(required = false) String deleteCode) {
        int examId = getSessionExamId(request);
        MarkGroup group = groupService.findOne(examId, subjectCode, number);
        if (group == null) {
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group";
        }
        Exam exam = examService.findById(examId);
        School school = schoolService.findById(exam.getSchoolId());
        boolean warn = systemCache.isGroupDeleteWarn() ? true : school.isGroupDeleteCheck();
        if (warn && group.getLibraryCount() != 0 && group.getLeftCount() == 0 && !markDeleteCode.equals(deleteCode)) {
            addMessage(redirectAttributes, "删除分组授权码不正确");
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group";
        }
        if (lockService.trylock(LockType.GROUP_DELETE, group.getExamId(), group.getSubjectCode(), group.getNumber())) {
            taskExecutor.submit(new MarkGroupDeleteThread(group, markService, lockService));
            RequestUtils.setLog(request, "开始删除分组，subjectCode：" + subjectCode + " number:" + number);
        }

        // try {
        // lockService.waitlock(LockType.EXAM_SUBJECT, group.getExamId(),
        // group.getSubjectCode());
        // lockService.waitlock(LockType.GROUP, group.getExamId(),
        // group.getSubjectCode(), group.getNumber());
        // markService.deleteGroup(group);
        // RequestUtils.setLog(request, "删除成功，subjectCode：" + subjectCode +
        // " number:" + number);
        // } catch (Exception e) {
        // log.error("delete group error", e);
        // throw new RuntimeException("删除大题失败", e);
        // } finally {
        // lockService.unlock(LockType.GROUP, group.getExamId(),
        // group.getSubjectCode(), group.getNumber());
        // lockService.unlock(LockType.EXAM_SUBJECT, group.getExamId(),
        // group.getSubjectCode());
        // }

        redirectAttributes.addAttribute("subjectCode", subjectCode);
        redirectAttributes.addAttribute("number", number);
        return "redirect:/admin/exam/group";
    }

    @Logging(menu = "修改大题", type = LogType.UPDATE)
    @SuppressWarnings("unchecked")
    @RequestMapping("/update")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    @Transactional
    public String update(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam Boolean reset,
            @RequestParam(required = false) String picList, @RequestParam(required = false) Double doubleRate,
            @RequestParam(required = false) Double arbitrateThreshold,
            @RequestParam(required = false) Integer thirdPolicy, @RequestParam(required = false) Integer scorePolicy,
            @RequestParam(required = false) MarkMode markMode, @RequestParam(required = false) Integer trialCount,
            @RequestParam(required = false, defaultValue = "false") Boolean sheetView,
            @RequestParam(required = false, defaultValue = "false") Boolean enableAllZero,
            @RequestParam(required = false) Integer[] questionIds,
            @RequestParam(required = false) String intervalScoreList,
            @RequestParam(required = false) String deleteCode) {
        int examId = getSessionExamId(request);
        Exam exam = examService.findById(examId);
        MarkGroup group = groupService.findOne(examId, subjectCode, number);
        if (group != null) {
            try {
                if (questionIds != null && reset.booleanValue()) {
                    // advance update
                    School school = schoolService.findById(exam.getSchoolId());
                    boolean warn = systemCache.isGroupDeleteWarn() ? true : school.isGroupDeleteCheck();
                    if (warn && group.getLibraryCount() != 0 && group.getLeftCount() == 0
                            && !markDeleteCode.equals(deleteCode)) {
                        addMessage(redirectAttributes, "分组授权码不正确");
                        redirectAttributes.addAttribute("subjectCode", subjectCode);
                        redirectAttributes.addAttribute("number", number);
                        return "redirect:/admin/exam/group/edit-simple";
                    }
                    if (!checkSelective(examId, subjectCode, questionIds)) {
                        addMessage(redirectAttributes, "选做题不合法！选做题和非选做题不能在一组，选做题不能跨组或跨区设置");
                        redirectAttributes.addAttribute("subjectCode", subjectCode);
                        redirectAttributes.addAttribute("number", number);
                        return "redirect:/admin/exam/group/edit-simple";
                    }
                    List<ExamQuestion> questionList = new ArrayList<ExamQuestion>();
                    boolean selective = false;
                    for (Integer questionId : questionIds) {
                        ExamQuestion question = questionService.findById(questionId);
                        questionList.add(question);
                        if (selective == false && selectiveGroupService.findOne(examId, subjectCode,
                                question.getMainNumber()) != null) {
                            selective = true;
                        }
                    }
                    if (!questionList.isEmpty()) {
                        ScorePolicy policy = scorePolicy != null ? ScorePolicy.findByValue(scorePolicy) : null;
                        ThirdPolicy third = thirdPolicy != null ? ThirdPolicy.findByValue(thirdPolicy)
                                : ThirdPolicy.DISABLE;
                        try {
                            lockService.waitlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(),
                                    group.getNumber());
                            markService.updateGroup(group, questionList, policy, third, selective);
                        } catch (Exception e) {
                            log.error("update group error", e);
                            throw new RuntimeException("重置更新大题失败", e);
                        } finally {
                            lockService.unlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(),
                                    group.getNumber());
                        }
                    }
                } else {
                    // simple update
                    List<Double> intervalScores = buildDoubleList(intervalScoreList);
                    List<ExamQuestion> questionList = questionService
                            .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number);
                    if (intervalScores.size() == questionList.size()) {
                        for (int i = 0; i < questionList.size(); i++) {
                            ExamQuestion q = questionList.get(i);
                            q.setIntervalScore(intervalScores.get(i));
                            questionService.save(q);
                        }
                    }
                }
                // quick update
                picList = StringEscapeUtils.unescapeHtml(picList);
                JSONArray array = JSONArray.fromObject(picList);
                List<MarkConfigItem> list = JSONArray.toList(array, new MarkConfigItem(), new JsonConfig());
                if (list != null && !list.isEmpty()) {
                    groupService.updatePicList(examId, subjectCode, number, list);
                }
                groupService.updateDoubleRate(examId, subjectCode, number, doubleRate);
                groupService.updateArbitrateThreshold(examId, subjectCode, number, arbitrateThreshold);
                groupService.updateMarkMode(examId, subjectCode, number, markMode);
                if (sheetView != null) {
                    groupService.updateSheetView(examId, subjectCode, number, sheetView);
                }
                if (enableAllZero != null) {
                    groupService.updateEnableAllZero(examId, subjectCode, number, enableAllZero);
                }
                redirectAttributes.addAttribute("subjectCode", subjectCode);
                redirectAttributes.addAttribute("number", number);
                return "redirect:/admin/exam/group";

            } catch (Exception e) {
                log.error("edit markgroup error", e);
                addMessage(redirectAttributes, "参数有误");
                redirectAttributes.addAttribute("subjectCode", subjectCode);
                redirectAttributes.addAttribute("number", number);
                return "redirect:/admin/exam/group";
            }
        } else {
            addMessage(redirectAttributes, "找不到对应大题");
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            redirectAttributes.addAttribute("number", number);
            return "redirect:/admin/exam/group";
        }
    }

    private boolean checkSelective(Integer examId, String subjectCode, Integer[] questionIds) {
        // 无选做题分组
        List<SelectiveGroup> list = selectiveGroupService.findByExamIdAndSubjectCode(examId, subjectCode);
        if (list.size() == 0) {
            return true;
        }
        List<ExamQuestion> selectives = new ArrayList<ExamQuestion>();
        Set<Integer> selectiveIndexs = new HashSet<Integer>();
        Set<Integer> selectiveParts = new HashSet<Integer>();
        for (Integer questionId : questionIds) {
            ExamQuestion question = questionService.findById(questionId);
            for (SelectiveGroup selectiveGroup : list) {
                if (selectiveGroup.getMainNumber().equals(question.getMainNumber())) {
                    selectives.add(question);
                    selectiveIndexs.add(selectiveGroup.getSelectiveIndex());
                    selectiveParts.add(selectiveGroup.getSelectivePart());
                }
            }
        }
        // 无选做题
        if (selectives.size() == 0) {
            return true;
        }
        // 选做题和非选做题一组
        if (selectives.size() != questionIds.length) {
            return false;
        }
        // 一个评卷分组内只能有一个选做题分组的题
        if (selectiveIndexs.size() > 1) {
            return false;
        }
        // 一个评卷分组内只能有一个选做题分区的题
        if (selectiveParts.size() > 1) {
            return false;
        }
        return true;
    }

    @Logging(menu = "新增大题", type = LogType.ADD)
    @SuppressWarnings("unchecked")
    @RequestMapping("/insert")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    @Transactional
    public String insert(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam Integer[] questionIds,
            @RequestParam String picList, @RequestParam(required = false) Double doubleRate,
            @RequestParam(required = false) Double arbitrateThreshold,
            @RequestParam(required = false) Integer thirdPolicy, @RequestParam(required = false) Integer scorePolicy,
            @RequestParam(required = false) String markMode, @RequestParam(required = false) Integer trialCount,
            @RequestParam(required = false, defaultValue = "false") boolean sheetView,
            @RequestParam(defaultValue = "false") boolean enableAllZero) {
        int examId = getSessionExamId(request);
        Exam exam = examService.findById(examId);
        MarkGroup group = groupService.findOne(examId, subjectCode, number);
        if (group != null) {
            addMessage(redirectAttributes, "评卷分组序号不能重复");
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group/add";
        } else if (questionIds == null || questionIds.length <= 0) {
            addMessage(redirectAttributes, "题目不能为空");
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group/add";
        } else if (!checkSelective(examId, subjectCode, questionIds)) {
            addMessage(redirectAttributes, "选做题不合法！选做题和非选做题不能在一组，选做题不能跨组或跨区设置");
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group/add";
        } else {
            try {
                // create group
                // build picList
                List<MarkConfigItem> picConfigList = null;
                if (!exam.getType().equals(ExamType.MULTI_MEDIA)) {
                    picList = StringEscapeUtils.unescapeHtml(StringUtils.trimToNull(picList));
                    JSONArray array = JSONArray.fromObject(picList);
                    picConfigList = JSONArray.toList(array, new MarkConfigItem(), new JsonConfig());
                }
                if (questionIds != null && questionIds.length > 0) {
                    List<ExamQuestion> list = new ArrayList<ExamQuestion>();
                    BigDecimal totalScore = BigDecimal.ZERO;
                    boolean selective = false;
                    for (Integer questionId : questionIds) {
                        ExamQuestion question = questionService.findById(questionId);
                        question.setGroupNumber(number);
                        list.add(question);
                        totalScore = totalScore.add(BigDecimal.valueOf(question.getTotalScore()));
                        if (selective == false && selectiveGroupService.findOne(examId, subjectCode,
                                question.getMainNumber()) != null) {
                            selective = true;
                        }
                    }
                    if (trialCount != null && trialCount > 0) {
                        subjectService.updateTrialCount(examId, subjectCode, trialCount);
                    }
                    if (this.groupService.countByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.TRIAL) > 0) {
                        ExamSubject subject = subjectService.find(examId, subjectCode);
                        trialCount = subject.getTrialCount();
                    }
                    group = new MarkGroup(examId, subjectCode, number, picConfigList, totalScore.doubleValue(),
                            doubleRate, arbitrateThreshold, scorePolicy, markMode, trialCount, sheetView, enableAllZero,
                            thirdPolicy, selective);
                    // clear and replace exam_question
                    questionService.save(list);
                    groupService.save(group);

                    studentService.updateSubjectiveStatusAndScore(examId, subjectCode, SubjectiveStatus.UNMARK, 0,
                            null);
                    inspectHistoryService.deleteByExamIdAndSubjectCode(examId, subjectCode);
                    redirectAttributes.addAttribute("subjectCode", subjectCode);
                    return "redirect:/admin/exam/group";
                } else {
                    addMessage(redirectAttributes, "参数有误");
                    redirectAttributes.addAttribute("subjectCode", subjectCode);
                    return "redirect:/admin/exam/group/add";
                }
            } catch (Exception e) {
                log.error("add markgroup error", e);
                addMessage(redirectAttributes, "参数有误");
                redirectAttributes.addAttribute("subjectCode", subjectCode);
                return "redirect:/admin/exam/group/add";
            }
        }
    }

    @RequestMapping("/getPictureConfig")
    public String get(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer number) {
        int examId = getSessionExamId(request);
        ExamStudentSearchQuery query = new ExamStudentSearchQuery();
        query.setExamId(examId);
        query.setSubjectCode(subjectCode);
        query.setUpload(true);
        List<ExamStudent> examStudents = studentService.findByQuery(query).getResult();
        MarkGroup group = null;
        if (number != null) {
            group = groupService.findOne(examId, subjectCode, number);
        }
        if (examStudents.size() > 0) {
            model.addAttribute("picUrls", buildPicUrl(examStudents.get(0)));
            model.addAttribute("pictureConfig", buildPictureConfig(group));
            model.addAttribute("fileServer", fileService.getFileServer());
            return "modules/mark/picConfig";
        } else {
            addMessage(redirectAttributes, "参数有误");
            redirectAttributes.addAttribute("subjectCode", subjectCode);
            return "redirect:/admin/exam/group/add";
        }
    }

    private String buildPictureConfig(MarkGroup group) {
        String json = "";
        if (group != null) {
            try {
                ObjectMapper mapper = new ObjectMapper();
                json = mapper.writeValueAsString(group.getPictureConfigList());
            } catch (JsonProcessingException e) {
                // e.printStackTrace();
            }
        }
        return json;
    }

    private List<String> buildPicUrl(ExamStudent student) {
        return fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1, student.getSliceCount());
    }

    private List<Double> buildDoubleList(String content) {
        List<Double> list = new ArrayList<Double>();
        content = StringUtils.trimToNull(content);
        if (content != null) {
            String[] values = StringUtils.split(content, ",");
            if (values != null) {
                for (String value : values) {
                    try {
                        Double number = Double.valueOf(value);
                        if (number != null) {
                            list.add(number);
                        }
                    } catch (Exception e) {
                        continue;
                    }
                }
            }
        }
        return list;
    }

    @Logging(menu = "大题关闭", type = LogType.QUERY)
    @RequestMapping("/finish")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String finish(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer[] groupNumbers) {
        int examId = getSessionExamId(request);
        for (Integer number : groupNumbers) {
            MarkGroup group = groupService.findOne(examId, subjectCode, number);
            if (group == null) {
                continue;
            }
            try {
                lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
                if (group.getStatus() == MarkStatus.FORMAL && group.getLeftCount() == 0) {
                    groupService.updateStatus(examId, subjectCode, number, MarkStatus.FINISH, group.getStatus());
                }
            } finally {
                lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
            }
        }
        redirectAttributes.addAttribute("subjectCode", subjectCode);
        return "redirect:/admin/exam/group";
    }

    @Logging(menu = "设置试评数量", type = LogType.UPDATE)
    @RequestMapping(value = "/updateTrialCount", method = RequestMethod.POST)
    @ResponseBody
    public JSONObject updateTrialCount(HttpServletRequest request, @RequestParam String subjectCode,
            @RequestParam Integer trialCount) {
        int examId = getSessionExamId(request);
        ExamSubject subject = subjectService.find(examId, subjectCode);
        JSONObject obj = new JSONObject();
        if (subject == null) {
            obj.accumulate("success", false);
            obj.accumulate("message", "科目不能为空");
            return obj;
        }
        long groupCount = groupService.countByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.TRIAL);
        if (groupCount == 0) {
            obj.accumulate("success", false);
            obj.accumulate("message", "试评分组不能为空");
            return obj;
        }
        if (subject.getTrialCount() <= trialCount) {
            subjectService.updateTrialCount(examId, subjectCode, trialCount);
            obj.accumulate("success", true);
        } else {
            obj.accumulate("success", false);
            obj.accumulate("message", "设置数量请大于" + subject.getTrialCount());
        }
        return obj;
    }
}
