package cn.com.qmth.stmms.biz.exam.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.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.BaseQueryService;
import cn.com.qmth.stmms.biz.exam.dao.ExamQuestionDao;
import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;

@Service
public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> implements ExamQuestionService {

    @Autowired
    private ExamQuestionDao questionDao;

    @Transactional
    @Override
    public ExamQuestion save(ExamQuestion question) {
        return questionDao.save(question);
    }

    @Transactional
    @Override
    public void save(List<ExamQuestion> questionList) {
        questionDao.save(questionList);
    }

    @Transactional
    @Override
    public void updateMainTitle(Integer examId, String subjectCode, boolean objective, Integer mainNumber,
            String mainTitle) {
        questionDao.updateMainTitle(examId, subjectCode, objective, mainNumber, mainTitle);
    }

    @Transactional
    @Override
    public void deleteByExamAndSubjectAndObjective(Integer examId, String subjectCode, boolean objective) {
        questionDao.deleteByExamIdAndSubjectCodeAndObjective(examId, subjectCode, objective);
    }

    @Transactional
    @Override
    public void deleteByExamAndSubjectAndObjectiveAndGroupNumber(Integer examId, String subjectCode, boolean objective,
            Integer groupNumber) {
        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(examId, subjectCode, objective, groupNumber);
    }

    @Override
    public List<ExamQuestion> findByExamAndSubjectAndObjective(Integer examId, String subjectCode, boolean objective) {
        return questionDao.findByExamIdAndSubjectCodeAndObjective(examId, subjectCode, objective);
    }

    @Override
    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndPaperType(Integer examId, String subjectCode,
            boolean objective, String paperType) {
        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndPaperType(examId, subjectCode, objective, paperType,
                new Sort(Direction.ASC, "mainNumber", "subNumber"));
    }

    @Override
    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndGroupNumber(Integer examId, String subjectCode,
            boolean objective, Integer groupNumber) {
        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(examId, subjectCode, objective,
                groupNumber);
    }

    @Override
    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode,
            boolean objective, Integer mainNumber) {
        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
                mainNumber);
    }

    @Override
    public ExamQuestionSearchQuery findByQuery(ExamQuestionSearchQuery query) {
        checkQuery(query);
        Page<ExamQuestion> result = questionDao.findAll(buildSpecification(query), query);
        fillResult(result, query);
        return query;
    }

    @Override
    public long countByQuery(ExamQuestionSearchQuery query) {
        return questionDao.count(buildSpecification(query));
    }

    @Override
    public long countByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode, boolean objective,
            Integer mainNumber) {
        return questionDao.countByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
                mainNumber);
    }

    private Specification<ExamQuestion> buildSpecification(final ExamQuestionSearchQuery query) {
        return new Specification<ExamQuestion>() {

            @Override
            public Predicate toPredicate(Root<ExamQuestion> 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 (query.getSubjectCode() != null) {
                    predicates.add(cb.equal(root.get("subjectCode"), query.getSubjectCode()));
                }
                if (query.getPaperType() != null) {
                    predicates.add(cb.equal(root.get("paperType"), query.getPaperType()));
                } else if (query.getNullPaperType() != null && query.getNullPaperType().booleanValue()) {
                    predicates.add(cb.isNull(root.get("paperType")));
                }
                if (query.getObjective() != null) {
                    predicates.add(cb.equal(root.get("objective"), query.getObjective()));
                }
                if (query.getTotalCountGt() != null) {
                    predicates.add(cb.gt(root.get("totalCount").as(Integer.class), query.getTotalCountGt()));
                }
                if (query.getTotalScoreGt() != null) {
                    predicates.add(cb.gt(root.get("totalScore").as(Double.class), query.getTotalScoreGt()));
                }
                if (query.getZeroRateGt() != null) {
                    predicates.add(cb.ge(root.get("zeroCount").as(Integer.class),
                            cb.prod(root.get("totalCount").as(Integer.class), query.getZeroRateGt())));
                }
                return predicates.isEmpty() ? cb.conjunction()
                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }

    @Override
    public List<String> getPaperType(int examId, String subjectCode) {
        return questionDao.getPaperType(examId, subjectCode, true);
    }

}
