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

import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

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.ExamStudentPaper;
import cn.com.qmth.stmms.biz.exam.model.Marker;
import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentPaperService;
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.model.MarkLibrary;
import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
import cn.com.qmth.stmms.biz.mark.model.MarkStepDTO;
import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
import cn.com.qmth.stmms.biz.mark.model.SpecialTagDTO;
import cn.com.qmth.stmms.biz.mark.model.Task;
import cn.com.qmth.stmms.biz.mark.model.TrackDTO;
import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
import cn.com.qmth.stmms.biz.mark.service.TaskService;
import cn.com.qmth.stmms.biz.utils.CurrentTaskUtil;
import cn.com.qmth.stmms.biz.utils.MarkRedisUtil;
import cn.com.qmth.stmms.common.enums.LibraryStatus;
import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;

/**
 * 评卷任务服务实现
 * 
 * @author LS
 * 
 */
@Service("taskService")
public class TaskServiceImpl implements TaskService, ApplicationContextAware, InitializingBean {

    private static Logger log = LoggerFactory.getLogger(TaskServiceImpl.class);

    @Autowired
    private MarkLibraryService libraryService;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private ExamStudentPaperService studentPaperService;

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private ExamQuestionService questionService;

    private StringRedisTemplate redisTemplate;

    private ApplicationContext context;

    @Autowired
    private MarkTrackService trackService;

    @Autowired
    private MarkSpecialTagService markSpecialTagService;

    @Value("${use.redis}")
    private boolean useRedis;

    @Override
    public List<Task> findByQuery(MarkLibrarySearchQuery query) {
        List<Task> list = new LinkedList<Task>();
        query = libraryService.findByQuery(query);
        if (query.getCurrentCount() > 0) {
            for (MarkLibrary library : query.getResult()) {
                list.add(build(library));
            }
        }
        return list;
    }

    @Override
    public Task build(MarkLibrary library) {
        ExamStudent student = studentService.findByExamIdAndExamNumber(library.getExamId(), library.getExamNumber());

        Task task = new Task();
        task.setTagList(getMarkSpecialTagList(library.getId()));
        task.setExist(true);
        task.setStudentId(library.getExamNumber());
        task.setLibraryId(library.getId());
        task.setMarkStepList(buildMarkStep(library));
        task.setPictureUrls(PictureUrlBuilder.getSliceUrls(library.getExamId(), library.getCampusId(),
                library.getSubjectCode(), library.getExamNumber(), library.getPicStart(), library.getPicCount()));
        task.setSheetUrls(PictureUrlBuilder.getSheetUrls(library.getExamId(), library.getCampusId(),
                library.getSubjectCode(), library.getExamNumber(), student.getSheetCount()));
        task.setAnswerUrl(PictureUrlBuilder.getAnswerUrl(library.getExamId(), library.getSubjectCode()));
        task.setPaperUrl(PictureUrlBuilder.getPaperUrl(library.getExamId(), library.getSubjectCode()));
        task.setObjectiveScore(student != null ? student.getObjectiveScore() : 0);
        task.setMarkTime(library.getMarkerTime());
        task.setTags(library.getTags());
        if (library.getMarkerId() != null) {
            task.setMarkId(library.getMarkerId());
        }
        if (library.getMarkerScore() != null) {
            task.setTotalScore(library.getMarkerScore());
        }
        if (library.getMarkerScoreList() != null) {
            task.setScoreList(library.getMarkerScoreList());
        }
        if (library.getStatus() == LibraryStatus.QUEUED && library.getMarkerId() != null) {
            task.setBack(true);
        }
        return task;
    }

    private List<MarkStepDTO> buildMarkStep(MarkLibrary library) {
        List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
        List<ExamQuestion> sList = null;
        // 首先尝试获取考生专属试题结构
        ExamStudentPaper paper = studentPaperService.find(library.getExamId(), library.getExamNumber(), false);
        if (paper != null) {
            sList = paper.getQuestionList();
        }
        // 没有的情况下再去读科目通用试题结构
        if (sList == null || sList.isEmpty()) {
            sList = questionService.findByExamAndSubjectAndObjective(library.getExamId(), library.getSubjectCode(),
                    false);
        }
        int number = 0;
        for (ExamQuestion question : sList) {
            number++;

            MarkStepDTO step = new MarkStepDTO();
            step.setQuestionNumber(question.getQuestionNumber());
            step.setBlockId(question.getMainNumber());
            step.setNumber(number);
            step.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
            step.setTotalScore(question.getTotalScore());
            step.setDefaultScore(0d);
            step.setHasLevel(false);
            step.setMax(question.getTotalScore());
            step.setMin(0d);
            step.setInterval(question.getIntervalScore());
            step.setScoreList(question.getScoreListArray());
            // 增加阅卷轨迹列表获取
            addTrack(step, question, trackService.findByStudentId(library.getStudentId()));
            list.add(step);
        }
        return list;
    }

    private void addTrack(MarkStepDTO step, ExamQuestion question, List<MarkTrack> trackList) {
        String questionNumber = question.getQuestionNumber();
        for (MarkTrack track : trackList) {
            if (track.getQuestionNumber().equals(questionNumber)) {
                step.addTrack(new TrackDTO(track));
            }
        }
    }

    @Override
    @Transactional
    public boolean submit(Task task) {
        if (task == null) {
            return false;
        }
        try {
            submitLibrary(task);
            return true;
        } catch (Exception e) {
            log.error("task submit faile", e);
            return false;
        }
    }

    private void submitLibrary(Task task) {
        MarkLibrary library = updateLibrary(task);
        // 同步操作
        if (task.isProblem()) {
            // ProblemLibrary library = new ProblemLibrary();
            // library.setExamId(formallyLibrary.getExamId());
            // library.setCampusCode(formallyLibrary.getCampusCode());
            // library.setSubjectCode(formallyLibrary.getSubjectCode());
            // library.setLibraryId(formallyLibrary.getId());
            // library.setType(LibraryType.FORMALLY);
            // library.setBlockId(formallyLibrary.getBlockId());
            // library.setSecretNo(formallyLibrary.getSecretNo());
            // library.setReason(task.getReason());
            // library.setSubmitTime(new Date());
            // library.setSubmitter(task.getMarkId());
            // library.setStage(formallyLibrary.getMarkerCount());
            // library.setStatus(LibraryStatus.WAITING);
            // problemLibraryService.save(library);
        } else {
            // FormallyHistory history = new FormallyHistory();
            // history.setLibraryId(task.getLibraryId());
            // history.setScoreList(task.getScoreList());
            // history.setScorer(task.getMarkId());
            // history.setScoreTime(new Date());
            // history.setSpent(task.getSpent());
            // history.setStage(task.getMarkerCount());
            // history.setTotalScore(task.getTotalScore());
            // if (task.isSelf()) {
            // history.setType(HistoryType.SELF);
            // } else if (task.isBack()) {
            // history.setType(HistoryType.BACK);
            // } else {
            // history.setType(HistoryType.COMMON);
            // BlockTaskCountUtil.addBlockMarkedTask(stringRedisTemplate,
            // task.getBlockId(), task.getLibraryId());
            // }
            // formallyHistoryService.save(history);

            onTaskSubmit(library, task);
        }
    }

    private void onTaskSubmit(MarkLibrary library, Task task) {
        if (library.getStatus() == LibraryStatus.MARKED) {
            // ExamStudent student =
            // studentService.findById(library.getStudentId());
            // if (student != null) {
            // student.setSubjectiveScore(library.getMarkerScore());
            // student.setScoreList(library.getScoreList(), false);
            // studentService.save(student);
            // }
            studentService.updateSubjectiveScore(library.getStudentId(), library.getMarkerScore(),
                    ExamStudent.buildScoreList(library.getScoreList()));

            subjectService.updateMarkedCount(library.getExamId(), library.getSubjectCode(),
                    (int) libraryService.countByExamAndSubjectAndStatus(library.getExamId(), library.getSubjectCode(),
                            LibraryStatus.MARKED));

            Map<String, List<TrackDTO>> trackMap = task.getTrackMap();
            for (String questionNumber : trackMap.keySet()) {
                trackService.deleteByStudentIdAndQuestionNumber(library.getStudentId(), questionNumber);
                List<TrackDTO> list = trackMap.get(questionNumber);
                for (TrackDTO dto : list) {
                    trackService.save(dto.transform(library));
                }
            }

            SpecialTagDTO[] tagList = task.getTagList();
            markSpecialTagService.deleteByLibraryId(library.getId());
            if (tagList != null && tagList.length > 0) {
                for (SpecialTagDTO s : tagList) {
                    MarkSpecialTag markSpecialTag = new MarkSpecialTag();
                    markSpecialTag.setLibraryId(library.getId());
                    markSpecialTag.setTagName(s.getTagName());
                    markSpecialTag.setPositionX(s.getPositionX());
                    markSpecialTag.setPositionY(s.getPositionY());
                    markSpecialTagService.save(markSpecialTag);
                }
            }
        }
    }

    /**
     * 保存评卷任务
     * 
     * @param task
     * @return
     */
    private MarkLibrary updateLibrary(Task task) {
        MarkLibrary library = libraryService.findById(task.getLibraryId());
        library.setMarkerId(task.getMarkId());
        library.setMarkerTime(new Date());
        library.setTags(StringUtils.trimToNull(task.getTags()));
        library.setStatus(task.isProblem() ? LibraryStatus.EXCEPTION : LibraryStatus.MARKED);
        library.setMarkerDuration(task.getSpent());
        if (!task.isProblem()) {
            library.setMarkerScore(task.getTotalScore());
            library.setMarkerScoreList(task.getScoreList());
        }
        return libraryService.save(library);
    }

    @Override
    public boolean setCurrent(Marker marker, int libraryId) {
        if (useRedis) {
            if (!MarkRedisUtil.isCurrentTask(redisTemplate, marker.getExamId(), marker.getSubjectCode(), libraryId)) {
                MarkRedisUtil.addCurrentTask(redisTemplate, marker, libraryId);
                return true;
            } else {
                return false;
            }
        } else {
            return CurrentTaskUtil.add(marker, libraryId);
        }
    }

    @Override
    public void removeCurrent(Marker marker, int libraryId) {
        if (useRedis) {
            MarkRedisUtil.removeCurrentTask(redisTemplate, marker.getExamId(), marker.getSubjectCode(), libraryId);
        } else {
            CurrentTaskUtil.remove(marker, libraryId);
        }
    }

    @Override
    public int countCurrent(int examId, String subjectCode) {
        if (useRedis) {
            return MarkRedisUtil.countCurrentTask(redisTemplate, examId, subjectCode);
        } else {
            return CurrentTaskUtil.count(examId, subjectCode);
        }
    }

    @Override
    public int countCurrent(Marker marker) {
        if (useRedis) {
            return MarkRedisUtil.countCurrentTask(redisTemplate, marker);
        } else {
            return CurrentTaskUtil.count(marker);
        }
    }

    @Override
    public void clearCurrent(int examId, String subjectCode) {
        if (useRedis) {
            MarkRedisUtil.clearCurrentTask(redisTemplate, examId, subjectCode);
        } else {
            CurrentTaskUtil.clear(examId, subjectCode);
        }
    }

    @Override
    public void clearCurrent(Marker marker) {
        if (useRedis) {
            MarkRedisUtil.clearCurrentTask(redisTemplate, marker);
        } else {
            CurrentTaskUtil.clear(marker);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (useRedis) {
            this.redisTemplate = (StringRedisTemplate) context.getBean("stringRedisTemplate");
        }
    }

    /*
     * 客户端非正常情况下退出系统时，对内存中的没有及时清除的任务进行处理
     * 
     */
    @Override
    public void clearTaskMap(long cleanMapinterval) throws Exception {
        if (useRedis) {// Redis
            MarkRedisUtil.QuartzClearTask(redisTemplate, cleanMapinterval);
        } else {// 内存方式
            CurrentTaskUtil.QuartzClearMap(cleanMapinterval);
        }
    }

    public SpecialTagDTO[] getMarkSpecialTagList(Integer libraryId) {
        SpecialTagDTO[] specialTags = null;
        List<MarkSpecialTag> list = markSpecialTagService.findByLibraryId(libraryId);
        if (!CollectionUtils.isEmpty(list)) {
            specialTags = new SpecialTagDTO[list.size()];
            for (int i = 0; i < list.size(); i++) {
                SpecialTagDTO specialTagDTO = new SpecialTagDTO();
                specialTagDTO.setTagName(list.get(i).getTagName());
                specialTagDTO.setPositionX(list.get(i).getPositionX());
                specialTagDTO.setPositionY(list.get(i).getPositionY());
                specialTags[i] = specialTagDTO;
            }
        }
        return specialTags;
    }

}
