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

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 org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.ScoreRate;
import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
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.MarkGroupService;
import cn.com.qmth.stmms.biz.exam.service.ScoreRateService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.biz.utils.ScoreCalculateUtil;
import cn.com.qmth.stmms.biz.utils.ScoreInfo;
import cn.com.qmth.stmms.biz.utils.ScoreItem;
import cn.com.qmth.stmms.common.enums.LockType;

public class ScoreCalculateThread implements Runnable {

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

    private ExamStudentService studentService;

    private ExamQuestionService questionService;

    private ExamSubjectService subjectService;

    private MarkService markService;

    private MarkGroupService groupService;

    private ScoreRateService scoreRateService;

    private int examId;

    private LockService lockService;

    private Map<String, List<ExamQuestion>> objectiveMap;

    private Map<String, List<ExamQuestion>> subjectiveMap;

    private Map<String, Set<String>> campusNameMap;

    private Map<String, Double> scoreRateMap;

    public ScoreCalculateThread(int examId, LockService lockService, ExamStudentService studentService,
            ExamQuestionService questionService, ExamSubjectService subjectService, ScoreRateService scoreRateService,
            MarkService markService, MarkGroupService groupService) {
        this.examId = examId;
        this.lockService = lockService;
        this.studentService = studentService;
        this.questionService = questionService;
        this.subjectService = subjectService;
        this.scoreRateService = scoreRateService;
        this.markService = markService;
        this.groupService = groupService;
        this.objectiveMap = new HashMap<String, List<ExamQuestion>>();
        this.subjectiveMap = new HashMap<String, List<ExamQuestion>>();
        this.scoreRateMap = new HashMap<String, Double>();
        this.campusNameMap = new HashMap<String, Set<String>>();
    }

    @Override
    public void run() {
        log.info("start calculate for examId=" + examId);
        try {
            int pageNumber = 1;
            int pageSize = 1000;
            List<ExamStudent> list = studentService.findByExamIdAndUploadAndAbsent(examId, true, false, pageNumber,
                    pageSize);
            while (list != null && list.size() > 0) {
                for (ExamStudent student : list) {
                    calculate(student);
                }

                pageNumber++;
                list = studentService.findByExamIdAndUploadAndAbsent(examId, true, false, pageNumber, pageSize);
            }

            // 统计各科目下每题得分率,主观题评卷完成才统计
            double total = groupService.sumLibraryCount(examId);
            double finish = groupService.sumMarkedCount(examId);
            if (total == finish) {
                statistics();
            }
        } catch (Exception e) {
            log.error("calculate exception for examId=" + examId, e);
        } finally {
            lockService.unlock(LockType.EXAM, examId);
            log.info("finish calculate for examId=" + examId);
        }
    }

    private void statistics() {
        List<ExamSubject> examSubjects = subjectService.list(examId);
        for (ExamSubject examSubject : examSubjects) {
            Set<String> campusNameList = campusNameMap.get(examSubject.getCode());
            if (campusNameList == null) {
                continue;
            }
            for (String campusName : campusNameList) {
                List<ExamQuestion> questions = this.findQuestionList(examSubject.getCode(), false);
                Long count = studentService.countByExamIdAndSubjectCodeAndCampus(examId, examSubject.getCode(),
                        campusName, true, false);
                for (ExamQuestion examQuestion : questions) {
                    ScoreRate scoreRate = scoreRateService.findOne(examId, examSubject.getCode(), campusName, false,
                            examQuestion.getMainNumber(), examQuestion.getSubNumber());
                    if (scoreRate == null) {
                        scoreRate = new ScoreRate();
                        scoreRate.setExamId(examId);
                        scoreRate.setCampusName(campusName);
                        scoreRate.setSubjectCode(examSubject.getCode());
                        scoreRate.setMainNumber(examQuestion.getMainNumber());
                        scoreRate.setSubNumber(examQuestion.getSubNumber());
                        scoreRate.setObjective(examQuestion.isObjective());
                        scoreRate.setMainTitle(examQuestion.getMainTitle());
                        scoreRate.setTotalScore(examQuestion.getTotalScore());
                    }
                    String key = getKey(examSubject.getCode(), campusName, examQuestion.getQuestionNumber());
                    BigDecimal bg = new BigDecimal(scoreRateMap.get(key) * 1.00 / count);
                    double avgScore = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                    if (scoreRate.getAvgScore() == null || scoreRate.getAvgScore() != avgScore) {
                        scoreRate.setAvgScore(avgScore);
                    }
                    if (scoreRate.getTotalScore() != examQuestion.getTotalScore()) {
                        scoreRate.setTotalScore(examQuestion.getTotalScore());
                    }
                    scoreRateService.save(scoreRate);
                }
            }
        }
    }

    private void calculate(ExamStudent student) {
        try {
            ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
            ScoreInfo info = util.calculate(findQuestionList(student.getSubjectCode(), true), null);

            student.setObjectiveScore(info.getObjectiveScore());
            student.setScoreList(info.getScoreList(), true);

            studentService.save(student);

            // 增加主观题总分统计
            markService.scoreCalculate(student.getExamId(), student.getSubjectCode(), student.getId());

            // 主观题得分统计
            // 需要重新从数据库读取一遍考生信息
            statistics(studentService.findById(student.getId()));
        } catch (Exception e) {
            log.error("statistic error", e);
        }
    }

    private void statistics(ExamStudent student) {
        List<ScoreItem> items = student.getScoreList(false);
        if (items.isEmpty()) {
            return;
        }
        List<ExamQuestion> list = this.findQuestionList(student.getSubjectCode(), false);
        int count = list.size();
        for (int i = 0; i < count; i++) {
            ScoreItem scoreItem = items.size() > i ? items.get(i) : null;
            if (scoreItem != null) {
                String key = getKey(student.getSubjectCode(), student.getCampusName(), list.get(i).getQuestionNumber());
                Double totalScore = scoreRateMap.get(key);
                if (totalScore == null) {
                    scoreRateMap.put(key, scoreItem.getScore());
                } else {
                    scoreRateMap.put(key, totalScore + scoreItem.getScore());
                }
            }
        }
        Set<String> campusNames = campusNameMap.get(student.getSubjectCode());
        if (campusNames == null) {
            campusNames = new HashSet<>();
            campusNameMap.put(student.getSubjectCode(), campusNames);
        }
        campusNames.add(student.getCampusName());
    }

    private String getKey(String subjectCode, String campusName, String questionNumber) {
        List<String> list = new ArrayList<String>();
        list.add(subjectCode);
        list.add(campusName);
        list.add(questionNumber);
        return StringUtils.join(list, "-");
    }

    private List<ExamQuestion> findQuestionList(String subjectCode, boolean objective) {
        if (objective) {
            List<ExamQuestion> list = objectiveMap.get(subjectCode);
            if (list == null) {
                list = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, true);
                objectiveMap.put(subjectCode, list);
            }
            return list;
        } else {
            List<ExamQuestion> list = subjectiveMap.get(subjectCode);
            if (list == null) {
                list = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
                subjectiveMap.put(subjectCode, list);
            }
            return list;
        }
    }
}
