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

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

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.mark.dao.TrialLibraryDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialTagDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialTrackDao;
import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
import cn.com.qmth.stmms.biz.mark.model.TrialTag;
import cn.com.qmth.stmms.biz.mark.model.TrialTrack;
import cn.com.qmth.stmms.biz.mark.query.TrialLibrarySearchQuery;
import cn.com.qmth.stmms.biz.mark.service.TrialService;

@Service
public class TrialServiceImpl extends BaseQueryService<TrialLibrary> implements TrialService {

    @Autowired
    private TrialLibraryDao libraryDao;

    @Autowired
    private TrialTrackDao trackDao;

    @Autowired
    private TrialTagDao tagDao;

    @Override
    public TrialLibrary findById(Integer id) {
        return libraryDao.findOne(id);
    }

    @Override
    public List<TrialLibrary> findUnMarkedLibrary(Integer examId, String subjectCode, Integer groupNumber,
            Integer markerId, int pageNumber, int pageSize) {
        TrialLibrarySearchQuery query = new TrialLibrarySearchQuery();
        query.setPageNumber(pageNumber);
        query.setPageSize(pageSize);
        return libraryDao.findUnMarked(examId, subjectCode, groupNumber, query);
    }

    @Override
    public TrialLibrarySearchQuery findLibrary(final TrialLibrarySearchQuery query) {
        checkQuery(query);
        query.addSort("examNumber", Direction.ASC);
        Page<TrialLibrary> result = libraryDao.findAll(buildSpecification(query), query);
        fillResult(result, query);
        return query;
    }

    @Override
    public long countLibrary(Integer examId, String subjectCode, Integer groupNumber) {
        return libraryDao.countByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, groupNumber);
    }

    @Override
    public List<TrialLibrary> findHistory(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
            int pageNumber, int pageSize, Sort sort, Double markerScore) {
        BaseQuery<TrialLibrary> query = new BaseQuery<>();
        query.setPageNumber(pageNumber);
        query.setPageSize(pageSize);
        if (sort != null) {
            query.setSort(sort);
        } else {
            query.setSort(new Sort(Direction.DESC, "markerTime"));
        }
        if (markerScore != null) {
            return libraryDao.findByExamIdAndSubjectCodeAndGroupNumberAndMarkerIdAndMarkerScore(examId, subjectCode,
                    groupNumber, markerId, markerScore, query);
        } else {
            return libraryDao.findByExamIdAndSubjectCodeAndGroupNumberAndMarkerId(examId, subjectCode, groupNumber,
                    markerId, query);
        }
    }

    @Override
    public List<TrialTrack> findTrack(Integer libraryId) {
        return trackDao.findByPkLibraryId(libraryId);
    }

    @Override
    public List<TrialTag> findTag(Integer libraryId) {
        return tagDao.findByLibraryIdOrderByIdAsc(libraryId);
    }

    private Specification<TrialLibrary> buildSpecification(final TrialLibrarySearchQuery query) {
        return new Specification<TrialLibrary>() {

            @Override
            public Predicate toPredicate(Root<TrialLibrary> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
                List<Predicate> predicates = new LinkedList<Predicate>();
                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.getExamNumber())) {
                    predicates.add(cb.equal(root.get("examNumber"), query.getExamNumber()));
                }
                if (StringUtils.isNotBlank(query.getSecretNumber())) {
                    predicates.add(cb.equal(root.get("secretNumber"), query.getSecretNumber()));
                }
                if (query.getStudentId() != null) {
                    predicates.add(cb.equal(root.get("studentId"), query.getStudentId()));
                }
                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                        .size()]));
            }
        };
    }

    @Override
    public List<TrialLibrary> findHistory(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
            String secretNumber, int pageNumber, int pageSize, Sort sort) {
        BaseQuery<TrialLibrary> query = new BaseQuery<>();
        query.setPageNumber(pageNumber);
        query.setPageSize(pageSize);
        if (sort != null) {
            query.setSort(sort);
        } else {
            query.setSort(new Sort(Direction.DESC, "markerTime"));
        }
        return libraryDao.findByExamIdAndSubjectCodeAndGroupNumberAndMarkerIdAndSecretNumber(examId, subjectCode,
                groupNumber, markerId, secretNumber, query);
    }

    @Override
    public long countByMarkerId(Integer markerId) {
        return libraryDao.countByMarkerId(markerId);
    }

    @Override
    public List<TrialTrack> findTrackByStudentId(Integer studentId) {
        return trackDao.findByStudentId(studentId);
    }

    @Override
    public List<TrialTag> findTagByStudentId(Integer studentId) {
        return tagDao.findByStudentIdOrderByIdAsc(studentId);
    }

    @Override
    public long countMarkedLibrary(Integer examId, String subjectCode, Integer groupNumber) {
        return libraryDao.countMarked(examId, subjectCode, groupNumber);
    }

    @Transactional
    @Override
    public void save(List<TrialLibrary> trialList) {
        libraryDao.save(trialList);
    }
}
