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

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.admin.utils.StudentJsonUtils;
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.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.file.service.FileService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.utils.ScoreCalculateUtil;
import cn.com.qmth.stmms.biz.utils.ScoreInfo;
import cn.com.qmth.stmms.common.enums.ExamStatus;
import cn.com.qmth.stmms.common.enums.ExamType;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.ObjectiveStatus;

public class ScoreCalculateThread implements Runnable {

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

    private ExamStudentService studentService;

    private ExamQuestionService questionService;

    private int examId;

    private Set<String> subjectCodeSet;

    private LockService lockService;

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

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

    // private Map<String, ExamSubject> subjectMap;

    // private Map<String, List<MarkGroup>> groupMap;
    //
    // private MarkGroupService groupService;

    // private ExamSubjectService subjectService;

    private ExamService examService;

    private FileService fileService;

    private boolean examLock;

    public ScoreCalculateThread(int examId, Set<String> subjectCodeSet, LockService lockService,
            ExamStudentService studentService, ExamQuestionService questionService, ExamService examService,
            ExamSubjectService subjectService, FileService fileService, boolean examLock) {
        this.examId = examId;
        this.subjectCodeSet = subjectCodeSet != null ? subjectCodeSet : new HashSet<>();
        this.lockService = lockService;
        this.studentService = studentService;
        this.questionService = questionService;
        this.examService = examService;
        this.fileService = fileService;
        this.examLock = examLock;
        this.objectiveMap = new HashMap<>();
        this.subjectiveMap = new HashMap<>();
    }

    @Override
    public void run() {
        // 获取考试信息并判断状态
        Exam exam = examService.findById(examId);
        if (exam == null || exam.getStatus() != ExamStatus.START) {
            log.info("calculate exception for examId=" + examId + ", exam is null or status error");
            return;
        }
        log.info("start calculate for examId=" + examId + ", subjectCode count=" + subjectCodeSet.size());
        // 判断是否整个考试上锁
        if (examLock) {
            lockService.trylock(LockType.SCORE_CALCULATE, examId);
        }
        for (String subjectCode : subjectCodeSet) {
            // 尝试上锁，失败直接跳过
            if (!lockService.trylock(LockType.SCORE_CALCULATE, examId, subjectCode)) {
                log.warn("calculate locked for examId=" + examId + ", subjectCode=" + subjectCode);
                continue;
            }
            try {
                log.info("start calculate for examId=" + examId + ", subjectCode=" + subjectCode);
                int pageNumber = 1;
                int pageSize = 1000;
                List<ExamStudent> list = studentService.findByExamIdAndSubjectCode(examId, subjectCode, pageNumber,
                        pageSize);
                while (list != null && list.size() > 0) {
                    for (ExamStudent student : list) {
                        calculate(student, exam.getType());
                    }
                    pageNumber++;
                    list = studentService.findByExamIdAndSubjectCode(examId, subjectCode, pageNumber, pageSize);
                }
                examService.updateObjectiveStatus(examId, ObjectiveStatus.FINISH);
            } catch (Exception e) {
                log.error("calculate exception for examId=" + examId + ", subjectCode=" + subjectCode, e);
            } finally {
                lockService.unlock(LockType.SCORE_CALCULATE, examId, subjectCode);
                log.info("finish calculate for examId=" + examId + ", subjectCode=" + subjectCode);
            }
        }
        if (examLock) {
            lockService.unlock(LockType.SCORE_CALCULATE, examId);
        }
    }

    private void calculate(ExamStudent student, ExamType examType) {
        // 未上传、缺考、违纪的考生不统分
        if (!student.isUpload() || student.isAbsent() || student.isBreach()) {
            return;
        }
        try {
            List<ExamQuestion> oList = findQuestionList(student.getSubjectCode(), student.getPaperType(), true);
            if (ExamType.MULTI_MEDIA.equals(examType) && oList.size() > 0) {
                byte[] s = fileService.downloadJson(student.getExamId(), student.getSecretNumber());
                String studentJson = new String(s, "UTF-8");
                String answers = StudentJsonUtils.toAnswers(studentJson);
                student.setAnswers(answers.toUpperCase());
                studentService.save(student);
            }
            ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
            ScoreInfo info = util.calculate(oList, null);

            student.setObjectiveScore(info.getObjectiveScore());
            student.setScoreList(info.getScoreList(), true);
            // 更新客观题得分
            studentService.updateObjectiveScore(student.getId(), student.getObjectiveScore(),
                    student.getObjectiveScoreList());
            // 增加主观题总分统计
            // markService.scoreCalculate(student,
            // findMarkGroup(student.getSubjectCode()));
        } catch (Exception e) {
            log.error("calculate error for studentId=" + student.getId(), e);
        }
    }

    private List<ExamQuestion> findQuestionList(String subjectCode, String paperType, boolean objective) {
        if (objective) {
            String key = subjectCode + "_" + StringUtils.trimToEmpty(paperType);
            List<ExamQuestion> list = objectiveMap.get(key);
            if (list == null) {
                list = questionService.findByExamAndSubjectAndObjectiveAndPaperType(examId, subjectCode, true,
                        paperType);
                objectiveMap.put(key, 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;
        }
    }

    // private List<MarkGroup> findMarkGroup(String subjectCode) {
    // List<MarkGroup> list = groupMap.get(subjectCode);
    // if (list == null) {
    // list = groupService.findByExamAndSubject(examId, subjectCode);
    // groupMap.put(subjectCode, list);
    // }
    // return list;
    // }
}
