package cn.com.qmth.stmms.biz.exam.service.impl;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.com.qmth.stmms.biz.common.BaseQuery;
import cn.com.qmth.stmms.biz.common.BaseQueryService;
import cn.com.qmth.stmms.biz.exam.dao.MarkerDao;
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.query.MarkerSearchQuery;
import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
import cn.com.qmth.stmms.biz.exam.service.MarkerService;
import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
import cn.com.qmth.stmms.common.enums.LibraryStatus;

@Service("markerService")
public class MarkerServiceImpl extends BaseQueryService<Marker> implements MarkerService {

    @Autowired
    private MarkerDao markerDao;

    @Autowired
    private MarkLibraryDao libraryDao;

    @Autowired
    private MarkGroupService groupService;

    @Transactional
    @Override
    public Marker save(Marker marker) {
        Marker previous = findByLoginName(marker.getLoginName());
        if (previous == null || (marker.getId() != null && previous.getId().equals(marker.getId()))) {
            return markerDao.save(marker);
        } else {
            return null;
        }
    }

    @Override
    public Marker findById(Integer id) {
        return markerDao.findOne(id);
    }

    @Override
    public Marker findByLoginName(String loginName) {
        List<Marker> list = markerDao.findByLoginName(loginName);
        return list != null && list.size() > 0 ? list.get(0) : null;
    }

    @Override
    public List<Marker> findByExamAndSubjectAndGroup(int examId, String subjectCode, int number) {
        return markerDao.findByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, number, null);
    }

    @Override
    public long countByExam(int examId) {
        return markerDao.countByExamId(examId);
    }

    @Override
    public long countByExamAndSubjectAndGroup(int examId, String subjectCode, int number) {
        return markerDao.countByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, number);
    }

    @Transactional
    @Override
    public int batchCreate(ExamSubject subject, int groupNumber, int count, String password) {
        int result = 0;
        if (subject != null && count > 0) {
            if (groupNumber > 0) {
                result = saveMarker(subject, groupNumber, count, password);
            } else {
                List<MarkGroup> groups = groupService.findByExamAndSubject(subject.getExamId(), subject.getCode());
                for (MarkGroup markGroup : groups) {
                    result = result + saveMarker(subject, markGroup.getNumber(), count, password);
                }
            }
        }
        return result;
    }

    private int saveMarker(ExamSubject subject, int groupNumber, int count, String password) {
        int result = 0;
        int start = 1;
        Marker previous = findLastMarker(subject.getExamId(), subject.getCode());
        if (previous != null) {
            start = previous.getNumber() + 1;
        }
        int end = start + count - 1;
        for (; start <= end; start++) {
            Marker marker = new Marker();
            marker.setExamId(subject.getExamId());
            marker.setSubjectCode(subject.getCode());
            if (StringUtils.isEmpty(password)) {
                Random random = new Random();
                for (int i = 0; i < 6; i++) {
                    password += random.nextInt(10);
                }
                marker.setPassword(password);
                password = "";// 随机码生产后将密码置空
            } else {
                marker.setPassword(password);
            }
            marker.setEnable(true);
            marker.buildLoginName(start);
            marker.setLoginName(marker.getLoginName());
            marker.setName(marker.getLoginName());
            marker.setGroupNumber(groupNumber);
            marker.setClassCount(0);
            if (save(marker) != null) {
                result++;
            }
        }
        return result;
    }

    private Marker findLastMarker(int examId, String subjectCode) {
        Marker marker = null;
        List<Marker> list = markerDao.findByExamIdAndSubjectCode(examId, subjectCode,
                new BaseQuery<Marker>(1, 1, new Sort(Direction.DESC, "id")));
        if (list != null && !list.isEmpty()) {
            marker = list.get(0);
        }
        if (marker != null) {
            return Marker.parseLoginName(marker.getLoginName());
        } else {
            return null;
        }
    }

    @Override
    public List<Marker> getMarkCount(int examId) {
        List<Marker> list = new LinkedList<Marker>();
        List<Object[]> result = libraryDao.countMarkerAndStatus(examId, LibraryStatus.MARKED);
        if (result != null) {
            for (Object[] array : result) {
                try {
                    Marker marker = findById((int) array[0]);
                    marker.setMarkedCount((long) array[1]);
                    list.add(marker);
                } catch (Exception e) {
                }
            }
        }
        return list;
    }

    @Override
    public List<Marker> getMarkCount(int examId, String subjectCode) {
        List<Marker> list = new LinkedList<Marker>();
        List<Object[]> result = libraryDao.countMarkerAndStatus(examId, subjectCode, LibraryStatus.MARKED);
        if (result != null) {
            for (Object[] array : result) {
                try {
                    Marker marker = findById((int) array[0]);
                    marker.setMarkedCount((long) array[1]);
                    list.add(marker);
                } catch (Exception e) {
                }
            }
        }
        return list;
    }

    @Override
    public MarkerSearchQuery findByQuery(final MarkerSearchQuery query) {
        checkQuery(query);
        Page<Marker> result = markerDao.findAll(buildSpecification(query), query);
        fillResult(result, query);
        return query;
    }

    private Specification<Marker> buildSpecification(final MarkerSearchQuery query) {
        return new Specification<Marker>() {

            @Override
            public Predicate toPredicate(Root<Marker> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
                List<Predicate> predicates = new LinkedList<Predicate>();
                if (query.getId() != null) {
                    predicates.add(cb.equal(root.get("id"), query.getId()));
                }
                if (query.getExamId() != null) {
                    predicates.add(cb.equal(root.get("examId"), query.getExamId()));
                }
                if (StringUtils.isNotBlank(query.getSubjectCode())) {
                    predicates.add(cb.equal(root.get("subjectCode"), query.getSubjectCode()));
                }
                if (query.getGroupNumber() != null) {
                    predicates.add(cb.equal(root.get("groupNumber"), query.getGroupNumber()));
                }
                if (StringUtils.isNotBlank(query.getLoginName())) {
                    predicates.add(cb.equal(root.get("loginName"), query.getLoginName()));
                }
                if (StringUtils.isNotBlank(query.getName())) {
                    predicates.add(cb.equal(root.get("name"), query.getName()));
                }
                return predicates.isEmpty() ? cb.conjunction()
                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }

    @Override
    public List<Marker> findByStudentId(int studentId) {
        return markerDao.findByStudentId(studentId);
    }

    @Override
    public int batchSave(List<Marker> list) {
        markerDao.save(list);
        return list.size();
    }

    @Override
    public long countByLoginName(String loginName) {
        return markerDao.countByLoginName(loginName);
    }

    @Override
    public List<Marker> findMode(String commo) {
        return markerDao.findByMode(commo);
    }
}
