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

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 org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.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.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.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import cn.com.qmth.stmms.admin.dto.SelectivePartDTO;
import cn.com.qmth.stmms.admin.thread.SubjectiveCalculateThread;
import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
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.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.InspectedService;
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.lock.LockService;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.common.annotation.Logging;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.LogType;
import cn.com.qmth.stmms.common.enums.ScorePolicy;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

@Controller("selectiveGroupController")
@RequestMapping("/admin/exam/selectiveGroup")
public class SelectiveGroupController extends BaseExamController {

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

    @Autowired
    private ExamQuestionService questionService;

    @Autowired
    private MarkGroupService groupService;

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private SelectiveGroupService selectiveGroupService;

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

    @Autowired
    private LockService lockService;

    @Autowired
    private MarkService markService;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private MarkerService markerService;

    @Autowired
    private InspectedService inspectedService;

    @Logging(menu = "选做题查询", type = LogType.QUERY)
    @RequestMapping
    public String list(HttpServletRequest request, Model model, @RequestParam String subjectCode) {
        int examId = getSessionExamId(request);
        ExamSubject subject = subjectService.find(examId, subjectCode);
        List<SelectiveGroup> list = selectiveGroupService.findByExamIdAndSubjectCode(examId, subjectCode);
        Map<Integer, SelectiveGroup> map = new HashMap<Integer, SelectiveGroup>();
        for (SelectiveGroup selectiveGroup : list) {
            map.put(selectiveGroup.getMainNumber(), selectiveGroup);
        }
        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
        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());
                examQuestion.setScorePolicy(map.get(examQuestion.getMainNumber()).getScorePolicy().getValue());
            }
        }
        model.addAttribute("subject", subject);
        model.addAttribute("list", selectiveGroupService.findIndexByExamIdAndSubjectCode(examId, subjectCode));
        model.addAttribute("questions", questions);
        ScorePolicy[] scorePolicyList = { ScorePolicy.MAX, ScorePolicy.MIN };
        model.addAttribute("scorePolicyList", scorePolicyList);
        return "modules/exam/selectiveList";
    }

    @RequestMapping("/add")
    public String add(Model model, HttpServletRequest request, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer selectiveCount, @RequestParam Integer scorePolicy,
            @RequestParam Integer selectivePart, @RequestParam(required = false) String parts) {
        int examId = getSessionExamId(request);
        List<MarkGroup> groups = groupService.findByExamAndSubject(examId, subjectCode);
        if (groups != null && groups.size() > 0) {
            addMessage(redirectAttributes, "该科目已经存在分组，无法设置");
            return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
        }
        List<SelectivePartDTO> partList = getPartList(parts);
        if (checkTotalScore(examId, subjectCode, partList)) {
            addMessage(redirectAttributes, "选做题区分数必须一样");
            return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
        }
        ExamSubject subject = subjectService.find(examId, subjectCode);
        List<SelectiveGroup> list = selectiveGroupService.findByExamIdAndSubjectCode(examId, subjectCode);
        Map<Integer, SelectiveGroup> map = new HashMap<Integer, SelectiveGroup>();
        for (SelectiveGroup selectiveGroup : list) {
            map.put(selectiveGroup.getMainNumber(), selectiveGroup);
        }
        List<ExamQuestion> questions = questionService.findMainByExamAndSubjectAndObjective(examId, subjectCode, false);
        for (ExamQuestion examQuestion : questions) {
            if (map.containsKey(examQuestion.getMainNumber())) {
                examQuestion.setSelective(true);
                examQuestion.setSelectiveIndex(map.get(examQuestion.getMainNumber()).getSelectiveIndex());
            }
            for (SelectivePartDTO selectivePartDTO : partList) {
                if (selectivePartDTO.getMainNumbers().contains(examQuestion.getMainNumber())) {
                    examQuestion.setSelective(true);
                }
            }
        }
        model.addAttribute("selectiveCount", selectiveCount);
        model.addAttribute("selectivePart", selectivePart);
        model.addAttribute("selectivePartNo", partList.size() + 1);
        model.addAttribute("scorePolicy", scorePolicy);
        model.addAttribute("questions", questions);
        model.addAttribute("subject", subject);
        model.addAttribute("parts", parts);
        return "modules/exam/selectiveAdd";
    }

    private List<SelectivePartDTO> getPartList(String parts) {
        List<SelectivePartDTO> list = new ArrayList<SelectivePartDTO>();
        if (StringUtils.isNotBlank(parts)) {
            parts = StringEscapeUtils.unescapeHtml(parts);
            JSONArray array = JSONArray.fromObject(parts);
            for (int i = 0; i < array.size(); i++) {
                JSONObject part = array.getJSONObject(i);
                SelectivePartDTO dto = new SelectivePartDTO();
                dto.setSelectivePart(part.getInt("selectivePart"));
                List<Integer> mainNumbers = new ArrayList<Integer>();
                JSONArray mainNumberArr = part.getJSONArray("mainNumbers");
                for (Object object : mainNumberArr) {
                    mainNumbers.add(Integer.parseInt(object.toString()));
                }
                dto.setMainNumbers(mainNumbers);
                list.add(dto);
            }
        }
        return list;
    }

    @Logging(menu = "选做题新增", type = LogType.ADD)
    @RequestMapping("/save")
    @Transactional
    public String save(HttpServletRequest request, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer selectiveCount, @RequestParam Integer scorePolicy,
            @RequestParam Integer selectivePart, @RequestParam String parts) {
        int examId = getSessionExamId(request);
        if (selectiveCount >= selectivePart) {
            addMessage(redirectAttributes, "选做题数量不能大于已选择题目数量");
            return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
        }
        List<MarkGroup> groups = groupService.findByExamAndSubject(examId, subjectCode);
        if (groups != null && groups.size() > 0) {
            addMessage(redirectAttributes, "该科目已经存在分组，无法设置");
            return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
        }
        List<SelectivePartDTO> partList = getPartList(parts);
        if (checkTotalScore(examId, subjectCode, partList)) {
            addMessage(redirectAttributes, "选做题区分数必须一样");
            return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
        }
        int index = selectiveGroupService.findMaxIndexByExamIdAndSubjectCode(examId, subjectCode);
        List<SelectiveGroup> list = new ArrayList<SelectiveGroup>();
        for (SelectivePartDTO part : partList) {
            for (Integer mainNumber : part.getMainNumbers()) {
                list.add(new SelectiveGroup(examId, subjectCode, mainNumber, index + 1, selectiveCount, part
                        .getSelectivePart(), ScorePolicy.findByValue(scorePolicy)));
            }
        }
        selectiveGroupService.save(list);
        subjectService.updateSelective(examId, subjectCode, true);
        subjectService.updateScore(examId, subjectCode, false,
                questionService.sumTotalScore(examId, subjectCode, false));
        return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
    }

    private boolean checkTotalScore(int examId, String subjectCode, List<SelectivePartDTO> parts) {
        List<ExamQuestion> questions = questionService.findMainByExamAndSubjectAndObjective(examId, subjectCode, false);
        Map<Integer, ExamQuestion> map = new HashMap<Integer, ExamQuestion>();
        for (ExamQuestion examQuestion : questions) {
            map.put(examQuestion.getMainNumber(), examQuestion);
        }
        Set<Double> scores = new HashSet<Double>();
        for (SelectivePartDTO part : parts) {
            double totalScore = 0;
            for (Integer mainNumber : part.getMainNumbers()) {
                totalScore = totalScore + map.get(mainNumber).getTotalScore();
            }
            scores.add(totalScore);
        }
        return scores.size() > 1;
    }

    @Logging(menu = "删除选做题", type = LogType.DELETE)
    @RequestMapping("/delete")
    @Transactional
    public String delete(HttpServletRequest request, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer selectiveIndex) {
        int examId = getSessionExamId(request);
        List<MarkGroup> groups = groupService.findByExamAndSubject(examId, subjectCode);
        if (groups != null && groups.size() > 0) {
            addMessage(redirectAttributes, "该科目已经存在分组，无法设置");
            return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
        }
        selectiveGroupService.deleteByExamIdAndSubjectCodeAndSelectiveIndex(examId, subjectCode, selectiveIndex);
        return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
    }

    @Logging(menu = "修改选做题区分规则", type = LogType.UPDATE)
    @RequestMapping("/edit")
    @Transactional
    public String edit(HttpServletRequest request, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode, @RequestParam Integer selectiveIndex, @RequestParam Integer policy) {
        int examId = getSessionExamId(request);
        if (lockService.isLocked(LockType.SCORE_CALCULATE, examId, subjectCode)) {
            addMessage(redirectAttributes, "正在统分中，请稍后再试");
            return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
        }
        selectiveGroupService.updateScorePolicy(examId, subjectCode, selectiveIndex, ScorePolicy.findByValue(policy));
//        List<Integer> studentList = studentService.findIdByExamIdAndSubjectCodeAndSubjectiveStatus(examId, subjectCode,
//                SubjectiveStatus.UNMARK, SubjectiveStatus.MARKED);
        SubjectiveCalculateThread thread = new SubjectiveCalculateThread(examId, subjectCode, markService,
                lockService, questionService, groupService, markerService, inspectedService,studentService);
        taskExecutor.submit(thread);
        return "redirect:/admin/exam/selectiveGroup?subjectCode=" + subjectCode;
    }

}
