package cn.com.qmth.stmms.biz.mark.service.Impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.com.qmth.stmms.biz.campus.model.Campus;
import cn.com.qmth.stmms.biz.exam.dao.ExamQuestionDao;
import cn.com.qmth.stmms.biz.exam.dao.MarkGroupDao;
import cn.com.qmth.stmms.biz.exam.dao.MarkerDao;
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.Marker;
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.mark.dao.ArbitrateHistoryDao;
import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
import cn.com.qmth.stmms.biz.mark.dao.MarkSpecialTagDao;
import cn.com.qmth.stmms.biz.mark.dao.MarkTrackDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialHistoryDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialLibraryDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialTagDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialTrackDao;
import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
import cn.com.qmth.stmms.biz.mark.model.MarkResult;
import cn.com.qmth.stmms.biz.mark.model.TrialHistory;
import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
import cn.com.qmth.stmms.biz.utils.ScoreItem;
import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
import cn.com.qmth.stmms.common.enums.HistoryStatus;
import cn.com.qmth.stmms.common.enums.LibraryStatus;
import cn.com.qmth.stmms.common.enums.MarkStatus;
import cn.com.qmth.stmms.common.enums.ScorePolicy;

/**
 * 与评卷相关的所有修改操作(非传播性的新增操作除外)，全部汇总到这里进行集中控制
 * 
 * @author luoshi
 *
 */
@Service("markService")
public class MarkServiceImpl implements MarkService {

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

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private MarkGroupDao groupDao;

    @Autowired
    private ExamQuestionDao questionDao;

    @Autowired
    private MarkerDao markerDao;

    @Autowired
    private MarkLibraryDao libraryDao;

    @Autowired
    private ArbitrateHistoryDao arbitrateDao;

    @Autowired
    private MarkTrackDao trackDao;

    @Autowired
    private MarkSpecialTagDao specialTagDao;

    @Autowired
    private TrialLibraryDao trialLibraryDao;

    @Autowired
    private TrialHistoryDao trialHistoryDao;

    @Autowired
    private TrialTrackDao trialTrackDao;

    @Autowired
    private TrialTagDao trialTagDao;

    @Autowired
    private ExamService examService;

    /**
     * 某个大题已申请的评卷任务数量
     * 
     * @param group
     * @return
     */
    @Override
    public int applyCount(MarkGroup group) {
        int count = 0;
        if (group.getStatus() == MarkStatus.TRIAL) {
            count = TrialTaskUtil.count(group.getExamId(), group.getSubjectCode(), group.getNumber());
        } else if (group.getStatus() == MarkStatus.FORMAL) {
            count = FormalTaskUtil.count(group.getExamId(), group.getSubjectCode(), group.getNumber());
        }
        return count;
    }

    /**
     * 某个评卷员已申请的评卷任务数量
     * 
     * @param marker
     * @param status
     * @return
     */
    @Override
    public int applyCount(Marker marker) {
        MarkGroup group = marker.getGroup();
        if (group == null) {
            group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        }
        if (group != null) {
            if (group.getStatus() == MarkStatus.FORMAL) {
                return FormalTaskUtil.count(marker);
            } else if (group.getStatus() == MarkStatus.TRIAL) {
                return TrialTaskUtil.count(marker);
            }
        }
        return 0;
    }

    /**
     * 某个评卷员已完成的评卷任务数量
     * 
     * @param marker
     * @param status
     * @return
     */
    @Override
    public long markedCount(Marker marker) {
        MarkGroup group = marker.getGroup();
        if (group == null) {
            group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        }
        if (group != null) {
            if (group.getStatus() == MarkStatus.TRIAL) {
                return trialHistoryDao.countByMarkerId(marker.getId());
            } else {
                return libraryDao.countByMarkerAndStatus(marker.getId(), LibraryStatus.MARKED,
                        LibraryStatus.ARBITRATED);
            }
        }
        return 0;
    }

    /**
     * 释放某个大题的锁定任务
     * 
     * @param group
     */
    @Override
    public void releaseByGroup(MarkGroup group) {
        if (group.getStatus() == MarkStatus.TRIAL) {
            TrialTaskUtil.clear(group.getExamId(), group.getSubjectCode(), group.getNumber());
        } else if (group.getStatus() == MarkStatus.FORMAL) {
            FormalTaskUtil.clear(group.getExamId(), group.getSubjectCode(), group.getNumber());
        }
    }

    /**
     * 重置某个大题的所有评卷任务
     * 
     * @param group
     */
    @Override
    @Transactional
    public void resetByGroup(MarkGroup group) {
        resetGroup(group);
    }

    /**
     * 删除某个大题
     * 
     * @param group
     */
    @Override
    @Transactional
    public void deleteGroup(MarkGroup group) {
        // 试评相关数据
        trialTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        trialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
        trialHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        trialLibraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        // 正评相关数据
        trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
        arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        // 评卷员数据
        markerDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        // 小题数据
        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                false, group.getNumber());

        releaseByGroup(group);
        groupDao.delete(group);
        // 科目总分
        subjectService.updateScore(group.getExamId(), group.getSubjectCode(), false,
                sumTotalScore(group.getExamId(), group.getSubjectCode()));
        // 需要重新统分
        examService.updateNeedCalculate(group.getExamId(), true);
    }

    /**
     * 修改某个大题给分步骤，并重置评卷任务
     * 
     * @param group
     */
    @Override
    @Transactional
    public void updateGroup(MarkGroup group, List<ExamQuestion> questionList, ScorePolicy policy) {
        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                false, group.getNumber());
        double totalScore = 0d;
        for (ExamQuestion question : questionList) {
            totalScore += question.getTotalScore();
            question.setGroupNumber(group.getNumber());
            questionDao.saveAndFlush(question);
        }
        groupDao.updateTotalScore(group.getExamId(), group.getSubjectCode(), group.getNumber(), totalScore);
        groupDao.updateScorePolicy(group.getExamId(), group.getSubjectCode(), group.getNumber(), policy);
        subjectService.updateScore(group.getExamId(), group.getSubjectCode(), false,
                sumTotalScore(group.getExamId(), group.getSubjectCode()));
        resetGroup(group);
    }

    /**
     * 评卷员申请领取某个正式评卷任务
     * 
     * @param library
     * @param marker
     */
    @Override
    public boolean applyLibrary(MarkLibrary library, Marker marker) {
        // 查询待领取任务时，已经做了多评同一studentId互斥处理
        // 首先判断多评情况下，同一个studentId是否已被该评卷员处理过
        // if (libraryDao.countByStudentIdAndMarkerId(library.getStudentId(),
        // marker.getId()) > 0) {
        // return false;
        // }
        return FormalTaskUtil.add(marker, getApplyTaskId(library));
    }

    /**
     * 评卷员申请领取某个试评评卷任务
     * 
     * @param library
     * @param marker
     */
    @Override
    public boolean applyLibrary(TrialLibrary library, Marker marker) {
        return TrialTaskUtil.add(marker, getApplyTaskId(library, marker));
    }

    /**
     * 评卷员是否已领取了某个正式评卷任务
     * 
     * @param library
     * @param marker
     * @return
     */
    @Override
    public boolean hasApplied(MarkLibrary library, Marker marker) {
        return FormalTaskUtil.exists(marker, getApplyTaskId(library));
    }

    /**
     * 评卷员是否已领取了某个试评评卷任务
     * 
     * @param library
     * @param marker
     * @return
     */
    @Override
    public boolean hasApplied(TrialLibrary library, Marker marker) {
        return TrialTaskUtil.exists(marker, getApplyTaskId(library, marker));
    }

    /**
     * 释放某个评卷员已领取的正评任务
     * 
     * @param library
     * @param marker
     */
    @Override
    public void releaseLibrary(MarkLibrary library, Marker marker) {
        FormalTaskUtil.remove(marker, getApplyTaskId(library));
    }

    /**
     * 释放某个评卷员已领取的试评任务
     * 
     * @param library
     * @param marker
     */
    @Override
    public void releaseLibrary(TrialLibrary library, Marker marker) {
        TrialTaskUtil.remove(marker, getApplyTaskId(library, marker));
    }

    /**
     * 释放某个评卷员的所有锁定任务
     * 
     * @param marker
     * @param group
     */
    @Override
    public void releaseByMarker(Marker marker) {
        FormalTaskUtil.clear(marker);
        TrialTaskUtil.clear(marker);
    }

    /**
     * 重置某个评卷员
     * 
     * @param marker
     */
    @Override
    @Transactional
    public void resetMarker(Marker marker) {
        MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        if (group == null || !markerDao.exists(marker.getId())) {
            return;
        }
        if (group.getStatus() == MarkStatus.FORMAL) {
            // 仲裁和等待仲裁的任务不被重置
            trackDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
            specialTagDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
            libraryDao.resetByMarkerId(marker.getId(), LibraryStatus.WAITING, LibraryStatus.ARBITRATED,
                    LibraryStatus.WAIT_ARBITRATE);
            markerDao.resetById(marker.getId());
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            trialTagDao.deleteByMarkerId(marker.getId());
            trialTrackDao.deleteByMarkerId(marker.getId());
            trialHistoryDao.deleteByMarkerId(marker.getId());
        }
        updateMarkedCount(group);
        releaseByMarker(marker);
    }

    /**
     * 根据考生ID删除评卷任务
     * 
     * @param studentId
     */
    @Override
    @Transactional
    public void deleteByStudent(ExamStudent student) {
        // 正评相关数据
        trackDao.deleteByStudentId(student.getId());
        specialTagDao.deleteByStudentId(student.getId());
        arbitrateDao.deleteByStudentId(student.getId());
        libraryDao.deleteByStudentId(student.getId());
        // 试评相关数据
        trialTrackDao.deleteByStudentId(student.getId());
        trialTagDao.deleteByStudentId(student.getId());
        trialHistoryDao.deleteByStudentId(student.getId());
        trialLibraryDao.deleteByStudentId(student.getId());

        updateAllCount(student.getExamId(), student.getSubjectCode());
    }

    /**
     * 评卷员提交评卷任务
     * 
     * @param task
     * @param marker
     * @return
     */
    @Override
    @Transactional
    public boolean submitTask(MarkResult result, Marker marker) {
        // 判断大题是否存在/评卷是否结束
        MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        if (group == null || group.getStatus() == MarkStatus.FINISH
                || group.getStatus().getValue() != result.getStatusValue()) {
            return false;
        }

        // 根据评卷状态选择读取不同的评卷任务
        if (group.getStatus() == MarkStatus.FORMAL) {
            MarkLibrary library = libraryDao.findOne(result.getLibraryId());
            if (library != null && library.getExamId().equals(group.getExamId())
                    && library.getSubjectCode().equals(group.getSubjectCode())
                    && library.getGroupNumber().equals(group.getNumber())
                    && result.getTotalScore() <= group.getTotalScore()
                    && StringUtils.isNotBlank(result.getScoreList())) {
                if (submitLibrary(library, marker, group, result)) {
                    updateMarkedCount(group);
                    releaseLibrary(library, marker);
                    return true;
                }
            }
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            TrialLibrary library = trialLibraryDao.findOne(result.getLibraryId());
            if (library != null && library.getExamId().equals(group.getExamId())
                    && library.getSubjectCode().equals(group.getSubjectCode())
                    && library.getGroupNumber().equals(group.getNumber())
                    && result.getTotalScore() <= group.getTotalScore()
                    && StringUtils.isNotBlank(result.getScoreList())) {
                TrialHistory history = new TrialHistory();
                history.setExamId(library.getExamId());
                history.setSubjectCode(library.getSubjectCode());
                history.setGroupNumber(library.getGroupNumber());
                history.setLibraryId(library.getId());
                history.setStudentId(library.getStudentId());
                history.setMarkerId(marker.getId());
                history.setMarkerTime(new Date());
                history.setMarkerScore(result.getTotalScore());
                history.setMarkerScoreList(result.getScoreList());
                history = trialHistoryDao.save(history);
                if (history != null) {
                    if (result.getTrackList() != null) {
                        trialTrackDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
                        trialTrackDao.save(result.getTrackList(history));
                    }
                    if (result.getTagList() != null) {
                        trialTagDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
                        trialTagDao.save(result.getTagList(history));
                    }
                    updateMarkedCount(group);
                    releaseLibrary(library, marker);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 评卷员提交某个正评任务
     * 
     * @param library
     * @param marker
     * @param trackMap
     * @param tagList
     */
    private boolean submitLibrary(MarkLibrary library, Marker marker, MarkGroup group, MarkResult result) {
        // 非本人领取的待评任务
        if (library.getStatus() == LibraryStatus.WAITING && !hasApplied(library, marker)) {
            return false;
        }
        // 非本人的回评任务
        if (library.getStatus() == LibraryStatus.MARKED && !library.getMarkerId().equals(marker.getId())) {
            return false;
        }
        // 是否多评情况下已处理过该考生评卷任务
        if (libraryDao.countByStudentIdAndMarkerIdAndIdNotEqual(library.getStudentId(), library.getMarkerId(),
                library.getId()) > 0) {
            return false;
        }
        // 尝试提交评卷结果
        Date now = new Date();
        if (libraryDao.updateMarkerResult(library.getId(), LibraryStatus.MARKED, marker.getId(), result.getTotalScore(),
                result.getScoreList(), now, result.getSpent(), null, LibraryStatus.WAITING,
                LibraryStatus.MARKED) == 0) {
            // 条件不符更新失败，直接返回
            return false;
        }
        // 保存阅卷轨迹
        if (result.getTrackList() != null) {
            trackDao.deleteByLibraryId(library.getId());
            trackDao.save(result.getTrackList(library, marker));
        }
        // 保存特殊标记
        if (result.getTagList() != null) {
            specialTagDao.deleteByLibraryId(library.getId());
            specialTagDao.save(result.getSpecialTagList(library, marker));
        }
        // 判断多评模式下是否需要仲裁
        ArbitrateHistory history = null;
        if (group.getArbitrateThreshold() != null && group.getArbitrateThreshold() > 0) {
            // 多评模式
            List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumberAndStatus(library.getStudentId(),
                    library.getGroupNumber(), LibraryStatus.MARKED);
            for (MarkLibrary other : list) {
                if (other.getId().equals(library.getId()) || other.getHeaderScore() != null) {
                    // 本评卷任务或组长已打分，则跳过该任务
                    continue;
                }
                if (Math.abs(other.getMarkerScore() - result.getTotalScore()) > group.getArbitrateThreshold()) {
                    // 分差超过阀值，触发仲裁
                    history = new ArbitrateHistory();
                    history.setExamId(library.getExamId());
                    history.setSubjectCode(library.getSubjectCode());
                    history.setGroupNumber(library.getGroupNumber());
                    history.setStudentId(library.getStudentId());
                    history.setExamNumber(library.getExamNumber());
                    history.setStatus(HistoryStatus.WAITING);
                    history.setCreateTime(now);
                    break;
                }
            }
        }
        if (history != null) {
            // 保存仲裁记录
            arbitrateDao.save(history);
            // 触发仲裁后续状态更新
            libraryDao.updateByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber(),
                    LibraryStatus.WAIT_ARBITRATE);
        }
        return true;
    }

    /**
     * 管理员/组长打回某个评卷任务<br>
     * 暂时不用到BACKED状态，直接等同于重置该评卷任务
     * 
     * @param library
     */
    @Override
    @Transactional
    public boolean backLibrary(MarkLibrary library) {
        if (libraryDao.resetById(library.getId(), LibraryStatus.WAITING, LibraryStatus.MARKED) > 0) {
            trackDao.deleteByLibraryId(library.getId());
            specialTagDao.deleteByLibraryId(library.getId());
            updateMarkedCount(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
            return true;
        } else {
            return false;
        }
    }

    /**
     * 管理员/组长重置某个试评任务
     * 
     * @param library
     */
    @Override
    @Transactional
    public boolean resetLibrary(TrialLibrary library) {
        if (library != null) {
            trialHistoryDao.deleteByLibraryId(library.getId());
            trialTrackDao.deleteByLibraryId(library.getId());
            trialTagDao.deleteByLibraryId(library.getId());
            return true;
        } else {
            return false;
        }
    }

    /**
     * 管理员/组长处理仲裁卷
     * 
     * @param history
     */
    @Override
    @Transactional
    public void processArbitrate(ArbitrateHistory history) {
        if (arbitrateDao.exists(history.getId())) {
            arbitrateDao.saveAndFlush(history);
            libraryDao.updateHeaderResult(history.getStudentId(), history.getGroupNumber(), history.getUserId(),
                    history.getTotalScore(), history.getScoreList(), history.getUpdateTime(), LibraryStatus.ARBITRATED);
            updateMarkedCount(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
            scoreCalculate(studentService.findById(history.getStudentId()),
                    groupDao.findByExamIdAndSubjectCode(history.getExamId(), history.getSubjectCode()));
        }
    }

    /**
     * 对某个考生某个科目进行主观题统分
     * 
     * @param examId
     * @param subjectCode
     * @param studentId
     */
    @Override
    @Transactional
    public void scoreCalculate(ExamStudent student, List<MarkGroup> groups) {
        if (student == null || groups == null || groups.isEmpty()) {
            return;
        }
        List<ScoreItem> scoreList = new ArrayList<ScoreItem>();
        double totalScore = 0.0;
        // 循环所有大题
        for (MarkGroup group : groups) {
            if (group.getStatus().equals(MarkStatus.TRIAL)) {
                return;
            }
            if (calculateGroup(group, student.getId())) {
                totalScore += group.getMarkScore();
                scoreList.addAll(group.getMarkScoreDetail());
            } else {
                // 未评完直接返回
                return;
            }
        }
        // 全部评完，更新考生主观题得分
        student.setSubjectiveScore(totalScore);
        student.setScoreList(scoreList, false);
        studentService.updateSubjectiveScore(student.getId(), student.getSubjectiveScore(),
                student.getSubjectiveScoreList());
    }

    private boolean calculateGroup(MarkGroup group, Integer studentId) {
        double score = 0;
        List<ScoreItem> detail = new ArrayList<ScoreItem>();
        int count = 0;
        // 未设置算分策略的情况下，默认取平均分
        ScorePolicy policy = group.getScorePolicy() != null ? group.getScorePolicy() : ScorePolicy.AVG;
        List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(studentId, group.getNumber());
        if (list.isEmpty()) {
            return false;
        }
        for (MarkLibrary library : list) {
            if (library.getStatus() != LibraryStatus.MARKED && library.getStatus() != LibraryStatus.ARBITRATED) {
                // 有非完成状态的评卷任务，直接返回
                return false;
            }
            count++;
            Double current = (library.getHeaderScore() != null ? library.getHeaderScore() : library.getMarkerScore());
            List<ScoreItem> scores = library.getScoreList();
            if (count == 1) {
                // 首份评卷任务，直接取总分与明细
                score = current;
                detail = scores;
            } else {
                switch (policy) {
                case AVG:
                    // 直接累加
                    score += current;
                    for (int i = 0; i < detail.size(); i++) {
                        try {
                            ScoreItem item = detail.get(i);
                            ScoreItem other = scores.get(i);
                            item.setScore(item.getScore() + other.getScore());
                        } catch (Exception e) {
                            continue;
                        }
                    }
                    break;
                case MAX:
                    // 高分优先
                    if (current > score) {
                        score = current;
                        detail = scores;
                    }
                    break;
                case MIN:
                    // 低分优先
                    if (current < score) {
                        score = current;
                        detail = scores;
                    }
                    break;
                default:
                    break;
                }
            }
        }
        // 取平均分策略下，累计分数需要重新计算一次
        if (policy == ScorePolicy.AVG && count > 1) {
            score = score / count;
            for (int i = 0; i < detail.size(); i++) {
                ScoreItem item = detail.get(i);
                item.setScore(item.getScore() / count);
            }
        }
        group.setMarkScore(score);
        group.setMarkScoreDetail(detail);
        return true;
    }

    private void updateMarkedCount(Integer examId, String subjectCode, Integer groupNumber) {
        MarkGroup group = groupDao.findOne(examId, subjectCode, groupNumber);
        if (group != null) {
            updateMarkedCount(group);
        }
    }

    /**
     * 更新某个大题已评任务数量
     * 
     * @param group
     */
    @Override
    @Transactional
    public void updateMarkedCount(MarkGroup group) {
        if (group.getStatus() == MarkStatus.FORMAL || group.getStatus() == MarkStatus.FINISH) {
            groupDao.updateMarkedCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    (int) libraryDao.countByExamIdAndSubjectCodeAndGroupNumberAndStatus(group.getExamId(),
                            group.getSubjectCode(), group.getNumber(), LibraryStatus.MARKED, LibraryStatus.ARBITRATED));
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            groupDao.updateMarkedCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    (int) trialLibraryDao.countMarked(group.getExamId(), group.getSubjectCode(), group.getNumber()));
        }
    }

    /**
     * 更新某个大题评卷任务总量
     * 
     * @param group
     */
    @Override
    @Transactional
    public void updateLibraryCount(MarkGroup group) {
        if (group.getStatus() == MarkStatus.FORMAL || group.getStatus() == MarkStatus.FINISH) {
            group.setLibraryCount((int) libraryDao.countByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(),
                    group.getSubjectCode(), group.getNumber()));
            groupDao.updateLibraryCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    group.getLibraryCount());
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            group.setLibraryCount((int) trialLibraryDao.countByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(),
                    group.getSubjectCode(), group.getNumber()));
            groupDao.updateLibraryCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    group.getLibraryCount());
        }
    }

    /**
     * 更新某个科目所有大题评卷任务数量
     * 
     * @param examId
     * @param subjectCode
     * @param groupNumber
     */
    @Transactional
    @Override
    public void updateAllCount(Integer examId, String subjectCode) {
        List<MarkGroup> groups = groupDao.findByExamIdAndSubjectCode(examId, subjectCode);
        for (MarkGroup group : groups) {
            updateMarkedCount(group);
            updateLibraryCount(group);
        }
    }

    /**
     * 根据考生、学习中心、大题构造正式评卷任务
     * 
     * @param student
     * @param campus
     * @param group
     * @param subject
     */
    @Override
    @Transactional
    public void buildFormalLibrary(ExamStudent student, Campus campus, MarkGroup group, ExamSubject subject) {
        // 查询是否已创建评卷任务
        if (libraryDao.countByStudentIdAndGroupNumber(student.getId(), group.getNumber()) == 0) {
            MarkLibrary library = new MarkLibrary();
            library.setExamId(student.getExamId());
            library.setSubjectCode(student.getSubjectCode());
            library.setGroupNumber(group.getNumber());
            library.setCampusId(campus.getId());
            library.setStudentId(student.getId());
            library.setExamNumber(student.getExamNumber());
            library.setTaskNumber(1);
            library.setStatus(LibraryStatus.WAITING);
            libraryDao.save(library);
            // 开启双评时需要判断是否生成第二份评卷任务
            if (group.getDoubleRate() != null && group.getDoubleRate() > 0) {
                boolean needDouble = false;
                if (group.getDoubleRate() == 1) {
                    needDouble = true;
                } else {
                    double studentCount = subject.getUploadCount();
                    double libraryCount = group.getLibraryCount();
                    double doubleCount = libraryDao.countByExamIdAndSubjectCodeAndGroupNumberAndTaskNumber(
                            group.getExamId(), group.getSubjectCode(), group.getNumber(), 2);
                    int expectCount = (int) (studentCount * group.getDoubleRate());
                    // 随机数判断加入当前已经生成双评任务的比例加权
                    needDouble = doubleCount < expectCount
                            && ((studentCount - libraryCount + doubleCount) <= (expectCount - doubleCount)
                                    || Math.random() < group.getDoubleRate());
                }
                if (needDouble) {
                    library = new MarkLibrary();
                    library.setExamId(student.getExamId());
                    library.setSubjectCode(student.getSubjectCode());
                    library.setGroupNumber(group.getNumber());
                    library.setCampusId(campus.getId());
                    library.setStudentId(student.getId());
                    library.setExamNumber(student.getExamNumber());
                    library.setTaskNumber(2);
                    library.setStatus(LibraryStatus.WAITING);
                    libraryDao.save(library);
                }
            }
        }
        group.setBuildTime(student.getUploadTime());
        groupDao.updateBuildTime(group.getExamId(), group.getSubjectCode(), group.getNumber(), student.getUploadTime());
    }

    /**
     * 根据考生、学习中心、大题构造试评评卷任务
     * 
     * @param student
     * @param campus
     * @param group
     */
    @Override
    @Transactional
    public void buildTrialLibrary(ExamStudent student, Campus campus, MarkGroup group) {
        // 查询是否已创建评卷任务
        if (trialLibraryDao.countByStudentIdAndGroupNumber(student.getId(), group.getNumber()) == 0) {
            TrialLibrary library = new TrialLibrary();
            library.setExamId(student.getExamId());
            library.setSubjectCode(student.getSubjectCode());
            library.setGroupNumber(group.getNumber());
            library.setCampusId(campus.getId());
            library.setStudentId(student.getId());
            library.setExamNumber(student.getExamNumber());
            trialLibraryDao.save(library);
        }
    }

    /**
     * 领取正式评卷任务时，用来区分的唯一标识<br/>
     * 多评时同一个考生的多份任务不能被同一位评卷员领取
     * 
     * @param library
     * @return
     */
    private String getApplyTaskId(MarkLibrary library) {
        return library.getStudentId() + "_" + library.getGroupNumber();
    }

    /**
     * 领取试评评卷任务时，用来区分的唯一标识
     * 
     * @param library
     * @return
     */
    private String getApplyTaskId(TrialLibrary library, Marker marker) {
        return library.getId() + "_" + marker.getId();
    }

    /**
     * 重置评卷分组的连带操作
     * 
     * @param group
     */
    private void resetGroup(MarkGroup group) {
        groupDao.resetCount(group.getExamId(), group.getSubjectCode(), group.getNumber());
        if (group.getStatus() == MarkStatus.FORMAL) {
            trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
            arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            libraryDao.resetByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber(), LibraryStatus.WAITING);
            markerDao.resetByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            trialTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            trialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
            trialHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
        }
        releaseByGroup(group);
    }

    /**
     * 计算并更新指定评卷员的评卷质量指标
     * 
     * @param marker
     */
    @Override
    @Transactional
    public void updateQuality(Marker marker) {
        List<MarkLibrary> list = libraryDao.findByMarkerId(marker.getId());
        int finishCount = 0;
        int validCount = 0;
        double sumScore = 0;
        double tempScore = 0;
        double avgScore = 0;
        double stdevScore = 0;
        int sumSpent = 0;
        double avgSpent = 0;
        for (MarkLibrary library : list) {
            finishCount++;
            if (library.getStatus() == LibraryStatus.MARKED) {
                validCount++;
            }
            double score = library.getMarkerScore() != null ? library.getMarkerScore() : 0;
            int spent = library.getMarkerSpent() != null ? library.getMarkerSpent() : 0;

            sumScore += score;
            sumSpent += spent;
            if (finishCount > 0) {
                avgScore = sumScore / finishCount;
                avgSpent = sumSpent / finishCount;
                // 递归法计算标准差
                if (finishCount - 1 != 0) {
                    tempScore = tempScore
                            + 1.0 * (finishCount - 1) / finishCount * (score - avgScore) * (score - avgScore);
                    stdevScore = Math.sqrt(tempScore / (finishCount - 1));
                }
            }
        }
        markerDao.updateQualityById(marker.getId(), finishCount, validCount, avgSpent / 1000, avgScore, stdevScore);
    }

    private double sumTotalScore(Integer examId, String subjectCode) {
        Double score = groupDao.sumTotalScore(examId, subjectCode);
        return score != null ? score.doubleValue() : 0d;
    }

}
