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

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

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.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import cn.com.qmth.stmms.biz.common.BaseQueryService;
import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
import cn.com.qmth.stmms.common.enums.LibraryStatus;

@Service
public class MarkLibraryServiceImpl extends BaseQueryService<MarkLibrary> implements MarkLibraryService {

    @Autowired
    private MarkLibraryDao libraryDao;

    @Override
    public MarkLibrary findById(int id) {
        return libraryDao.findOne(id);
    }

    @Override
    public List<MarkLibrary> findByStudentAndGroup(int studentId, int groupNumber) {
        return libraryDao.findByStudentIdAndGroupNumber(studentId, groupNumber);
    }

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

    @Override
    public List<MarkLibrary> findUnMarked(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
            int pageNumber, int pageSize) {
        Set<LibraryStatus> statusSet = new HashSet<>();
        statusSet.add(LibraryStatus.WAITING);

        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setPageNumber(pageNumber);
        query.setPageSize(pageSize);

        return libraryDao.findUnMarked(examId, subjectCode, groupNumber, markerId, statusSet, query);
    }

    @Override
    public List<Integer> findTagCampusId(int examId) {
        return libraryDao.findTagCampusId(examId);
    }

    @Override
    public List<String> findTagSubjectCode(int examId) {
        return libraryDao.findTagSubjectCode(examId);
    }

    @Override
    public long countByExamAndSubjectAndGroupAndStatus(int examId, String subjectCode, int groupNumber,
            LibraryStatus status) {
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setExamId(examId);
        query.setSubjectCode(subjectCode);
        query.setGroupNumber(groupNumber);
        query.addStatus(status);
        return countByQuery(query);
    }

    @Override
    public long countByMarker(int markerId) {
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setMarkerId(markerId);
        // query.addStatus(LibraryStatus.MARKED);
        return countByQuery(query);
    }

    @Override
    public long countByQuery(final MarkLibrarySearchQuery query) {
        return libraryDao.count(buildSpecification(query));
    }

    @Override
    public MarkLibrarySearchQuery findByQuery(final MarkLibrarySearchQuery query) {
        checkQuery(query);
        Page<MarkLibrary> result = libraryDao.findAll(buildSpecification(query), query);
        fillResult(result, query);
        return query;
    }

    private Specification<MarkLibrary> buildSpecification(final MarkLibrarySearchQuery query) {
        return new Specification<MarkLibrary>() {

            @Override
            public Predicate toPredicate(Root<MarkLibrary> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
                List<Predicate> predicates = new LinkedList<Predicate>();
                if (query.getExamId() > 0) {
                    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.getCampusId() > 0) {
                    predicates.add(cb.equal(root.get("campusId"), query.getCampusId()));
                }
                if (query.getGroupNumber() > 0) {
                    predicates.add(cb.equal(root.get("groupNumber"), query.getGroupNumber()));
                }
                if (StringUtils.isNotBlank(query.getExamNumber())) {
                    predicates.add(cb.equal(root.get("examNumber"), query.getExamNumber()));
                }
                if (query.getStudentId() > 0) {
                    predicates.add(cb.equal(root.get("studentId"), query.getStudentId()));
                }
                if (query.getMarkerId() > 0) {
                    predicates.add(cb.equal(root.get("markerId"), query.getMarkerId()));
                }
                if (query.getStatusSet() != null && query.getStatusSet().size() > 0) {
                    List<Predicate> sub = new LinkedList<>();
                    for (LibraryStatus status : query.getStatusSet()) {
                        sub.add(cb.equal(root.get("status").as(LibraryStatus.class), status));
                    }
                    predicates.add(cb.or(sub.toArray(new Predicate[sub.size()])));
                }
                if (query.getTagNotNull() != null && query.getTagNotNull().booleanValue()) {
                    predicates.add(cb.isNotNull(root.get("tags")));
                }
                if (query.getTagId() > 0) {
                    predicates.add(cb.like(root.get("tags").as(String.class), "%" + query.getTagId() + "%"));
                }
                return predicates.isEmpty() ? cb.conjunction()
                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }

    @Override
    public List<MarkLibrary> findByStudentIdAndStatus(int studentId, LibraryStatus status) {
        return libraryDao.findByStudentIdAndStatus(studentId, status);
    }

}
