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

import java.util.ArrayList;
import java.util.Date;
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.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.distributpaper.dao.FormallyLibraryDao;
import cn.com.qmth.stmms.biz.distributpaper.model.FormallyLibrary;
import cn.com.qmth.stmms.biz.distributpaper.query.FormallyLibrarySearchQuery;
import cn.com.qmth.stmms.biz.distributpaper.service.FormallyLibraryService;
import cn.com.qmth.stmms.biz.exam.model.SubjectiveBlock;
import cn.com.qmth.stmms.biz.exam.service.SubjectiveBlockService;
import cn.com.qmth.stmms.common.enums.ClassType;
import cn.com.qmth.stmms.common.enums.LibraryStatus;

@Service
public class FormallyLibraryServiceImpl extends BaseQueryService<FormallyLibrary> implements FormallyLibraryService {

    @Autowired
    private FormallyLibraryDao formallyLibraryDao;

    @Autowired
    private SubjectiveBlockService subjectiveBlockService;

    @Transactional
    public FormallyLibrary save(FormallyLibrary formallyLibrary) {
        return formallyLibraryDao.save(formallyLibrary);
    }

    public FormallyLibrary findById(Integer id) {
        return formallyLibraryDao.findOne(id);
    }

    @Transactional
    public void deleteById(Integer id) {
        formallyLibraryDao.delete(id);
    }

    @Transactional
    public void delete(FormallyLibrary formallyLibrary) {
        formallyLibraryDao.delete(formallyLibrary);
    }

    public List<FormallyLibrary> findByExamIdAndBlockId(Integer examId, Integer blockId) {
        return formallyLibraryDao.findByExamIdAndBlockId(examId, blockId);
    }

    public long countByExamIdAndBlockId(Integer examId, Integer blockId) {
        FormallyLibrarySearchQuery query = new FormallyLibrarySearchQuery();
        query.setExamId(examId);
        query.setBlockId(blockId);
        return countByQuery(query);
    }

    public List<FormallyLibrary> findByExamIdAndBlockIdAndStatus(Integer examId, Integer blockId, LibraryStatus status) {
        return formallyLibraryDao.findByExamIdAndBlockIdAndStatus(examId, blockId, status);
    }

    public long countByExamIdAndBlockIdAndStatus(Integer examId, Integer blockId, LibraryStatus status) {
        FormallyLibrarySearchQuery query = new FormallyLibrarySearchQuery();
        query.setExamId(examId);
        query.setBlockId(blockId);
        query.setStatus(status);
        return countByQuery(query);
    }

    public List<FormallyLibrary> findByBlockIdAndSecretNo(Integer blockId, Integer secretNo) {
        return formallyLibraryDao.findByBlockIdAndSecretNo(blockId, secretNo);
    }

    public List<FormallyLibrary> findByExamIdAndBlockIdAndGetterAndStatus(int examId, int blockId, int getter,
            LibraryStatus status) {
        return formallyLibraryDao.findByExamIdAndBlockIdAndGetterAndStatus(examId, blockId, getter, status);
    }

    public long countByQuery(final FormallyLibrarySearchQuery query) {
        return formallyLibraryDao.count(new Specification<FormallyLibrary>() {

            @Override
            public Predicate toPredicate(Root<FormallyLibrary> 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 (query.getSubjectId() > 0) {
                    predicates.add(cb.equal(root.get("subjectId"), query.getSubjectId()));
                }
                if (query.getClassType() != null) {
                    predicates.add(cb.equal(root.get("classType").as(ClassType.class), query.getClassType()));
                }
                if (query.getBlockId() > 0) {
                    predicates.add(cb.equal(root.get("blockId"), query.getBlockId()));
                }
                if (query.getSecretNo() > 0) {
                    predicates.add(cb.equal(root.get("secretNo"), query.getSecretNo()));
                }
                if (query.getGetter() > 0) {
                    predicates.add(cb.equal(root.get("getter"), query.getGetter()));
                }
                if (query.getStatus() != null) {
                    predicates.add(cb.equal(root.get("status").as(LibraryStatus.class), query.getStatus()));
                }
                if (query.getMaxGetTime() != null) {
                    predicates.add(cb.lessThanOrEqualTo(root.get("getTime").as(Date.class), query.getMaxGetTime()));
                }
                if (query.getMinGetTime() != null) {
                    predicates.add(cb.greaterThan(root.get("getTime").as(Date.class), query.getMinGetTime()));
                }

                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                        .size()]));
            }
        });
    }

    public FormallyLibrarySearchQuery findByQuery(final FormallyLibrarySearchQuery query) {
        checkQuery(query);

        Page<FormallyLibrary> result = formallyLibraryDao.findAll(new Specification<FormallyLibrary>() {

            @Override
            public Predicate toPredicate(Root<FormallyLibrary> 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 (query.getSubjectId() > 0) {
                    predicates.add(cb.equal(root.get("subjectId"), query.getSubjectId()));
                }
                if (query.getClassType() != null) {
                    predicates.add(cb.equal(root.get("classType").as(ClassType.class), query.getClassType()));
                }
                if (query.getBlockId() > 0) {
                    predicates.add(cb.equal(root.get("blockId"), query.getBlockId()));
                }
                if (query.getSecretNo() > 0) {
                    predicates.add(cb.equal(root.get("secretNo"), query.getSecretNo()));
                }
                if (query.getGetter() > 0) {
                    predicates.add(cb.equal(root.get("getter"), query.getGetter()));
                }
                if (query.getStatus() != null) {
                    predicates.add(cb.equal(root.get("status").as(LibraryStatus.class), query.getStatus()));
                }
                if (query.getMaxGetTime() != null) {
                    predicates.add(cb.lessThanOrEqualTo(root.get("getTime").as(Date.class), query.getMaxGetTime()));
                }
                if (query.getMinGetTime() != null) {
                    predicates.add(cb.greaterThan(root.get("getTime").as(Date.class), query.getMinGetTime()));
                }

                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                        .size()]));
            }
        }, query);

        fillResult(result, query);
        return query;
    }

    @Override
    public List<FormallyLibrary> randomCheck(int examId, String subjectCode) {
        List<SubjectiveBlock> blockList = subjectiveBlockService.findBlockByExamIdAndSubjectCode(examId,subjectCode);
        List<FormallyLibrary> list = new ArrayList<FormallyLibrary>();
        for (SubjectiveBlock block : blockList) {
            List<FormallyLibrary> formallyList = formallyLibraryDao.findByExamIdAndBlockIdAndStatusAndIsChecked(examId,
                    block.getId(), LibraryStatus.MARKED, false);
            list.addAll(formallyList);
        }
        return list;
    }

    @Override
    public List<FormallyLibrary> findByExamIdAndSubjectCodeAndBlockIdAndSecretNoAndStatus(int examId,
            String subjectCode, int blockId, int secretNo, LibraryStatus status) {
        return formallyLibraryDao.findByExamIdAndSubjectCodeAndBlockIdAndSecretNoAndStatus(examId, subjectCode, blockId, secretNo, status);
    }
}
