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

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

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
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.PageImpl;
import org.springframework.data.domain.PageRequest;
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.model.ExamSubject;
import cn.com.qmth.stmms.biz.exam.model.Marker;
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.biz.mark.service.MarkTrackService;
import cn.com.qmth.stmms.common.enums.LibraryStatus;

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

    @Autowired
    private MarkLibraryDao libraryDao;

    @Autowired
    private MarkTrackService trackService;

    @PersistenceContext
    private EntityManager em;

    @Transactional
    @Override
    public void delete(MarkLibrary library) {
        libraryDao.delete(library);
        if (library != null) {
            trackService.deleteByStudentId(library.getStudentId());
        }
    }

    @Transactional
    @Override
    public void deleteByStudent(int studentId) {
        MarkLibrary library = findByStudentId(studentId);
        if (library != null) {
            delete(library);
        }
        trackService.deleteByStudentId(studentId);
    }

    @Transactional
    @Override
    public void deleteByExam(int examId) {
        libraryDao.deleteByExamId(examId);
        trackService.deleteByExamId(examId);
    }

    @Transactional
    @Override
    public MarkLibrary save(MarkLibrary library) {
        return libraryDao.save(library);
    }

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

    @Override
    public MarkLibrary findByStudentId(int studentId) {
        List<MarkLibrary> list = libraryDao.findByStudentId(studentId);
        return list != null && list.size() > 0 ? list.get(0) : null;
    }

    @Override
    public List<MarkLibrary> findByStatus(int examId, String subjectCode, LibraryStatus status, int pageNumber,
            int pageSize) {
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setPageNumber(pageNumber);
        query.setPageSize(pageSize);
        return libraryDao.findByExamIdAndSubjectCodeAndStatus(examId, subjectCode, status, query);
    }

    @Override
    @Transactional
    public void resetBySubject(ExamSubject subject) {
        libraryDao.resetByExamIdAndSubjectCode(subject.getExamId(), subject.getCode(), LibraryStatus.WAITING);
        trackService.deleteByExamIdAndSubjectCode(subject.getExamId(), subject.getCode());
    }

    @Override
    @Transactional
    public void resetByMarker(Marker marker) {
        libraryDao.resetByMarkerId(marker.getId(), LibraryStatus.WAITING);
        trackService.deleteByMarkerId(marker.getId());
    }

    @Override
    @Transactional
    public void resetById(int id) {
        libraryDao.resetById(id, LibraryStatus.WAITING);
        MarkLibrary library = findById(id);
        if (library != null) {
            trackService.deleteByStudentId(library.getStudentId());
        }
    }

    @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 countByExam(int examId) {
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setExamId(examId);
        return countByQuery(query);
    }

    @Override
    public long countByExamAndStatus(int examId, LibraryStatus status) {
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setExamId(examId);
        query.setStatus(status);
        return countByQuery(query);
    }

    @Override
    public long countByExamAndSubject(int examId, String subjectCode) {
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setExamId(examId);
        query.setSubjectCode(subjectCode);
        return countByQuery(query);
    }

    @Override
    public long countByExamAndSubjectAndStatus(int examId, String subjectCode, LibraryStatus status) {
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setExamId(examId);
        query.setSubjectCode(subjectCode);
        query.setStatus(status);
        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 (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.getStatus() != null) {
                    predicates.add(cb.equal(root.get("status").as(LibraryStatus.class), query.getStatus()));
                }
                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<Object[]> getMarkerInfo(Integer markerId, String subjectCode) {
        return libraryDao.getMarkerInfo(markerId, subjectCode);
    }

    @Override
    public List<Double> getAllScore(Integer id, String subjectCode) {
        return libraryDao.getAllScore(id, subjectCode);
    }

    @Override
    public List<String> getCodeListByMidGBCodeCount(int markerId, LibraryStatus status) {
        return libraryDao.findCodeListByMidGBCode(markerId, status);
    }

    @Override
    public long countByMidAndStatus(int markerId, LibraryStatus status) {
        return libraryDao.countByMidAndStatus(markerId, status);
    }

    @Override
    public long countByMidAndSubjectCodeStatus(int markerId, String subjectCode, LibraryStatus status) {
        return libraryDao.countByMidAndSubjectCodeStatus(markerId, subjectCode, status);
    }

    @Override
    public List<Object[]> getMarkerInfo(int markerId, LibraryStatus status) {
        return libraryDao.getMarkerInfo(markerId, status);
    }

    private Specification<MarkLibrary> buildSpecificationGroupBySubjectCode(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 (StringUtils.isNotBlank(query.getSubjectCode())) {
                    predicates.add(cb.equal(root.get("subjectCode"), query.getSubjectCode()));
                }
                if (query.getMarkerId() > 0) {
                    predicates.add(cb.equal(root.get("markerId"), query.getMarkerId()));
                }
                if (query.getStatus() != null) {
                    predicates.add(cb.equal(root.get("status").as(LibraryStatus.class), query.getStatus()));
                }
                cQuery.groupBy(root.get("subjectCode"));
                return predicates.isEmpty() ? cb.conjunction()
                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }

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

    @SuppressWarnings("unchecked")
    @Override
    public MarkLibrarySearchQuery getByCondition(final MarkLibrarySearchQuery query) {
        StringBuffer sql = new StringBuffer("SELECT m.* FROM m_library m WHERE 1=1 and m.status = 3");
        if (StringUtils.isNotBlank(query.getSubjectCode())) {
            sql.append(" and m.subject_code = '").append(query.getSubjectCode()).append("'");
        }
        if (query.getMarkerId() > 0) {
            sql.append(" and  m.marker_id =").append(query.getMarkerId());
        }
        sql.append(" group by m.subject_code");
        Query qCount = em.createNativeQuery(sql.toString(), MarkLibrary.class);
        int totalCount = qCount.getResultList().size();
        int pageNumber = query.getPageNumber() - 1;
        if (pageNumber > 0) {
            pageNumber = pageNumber * query.getPageSize();
        }
        sql.append(" limit ").append(pageNumber).append(",").append(query.getPageSize());
        Query q = em.createNativeQuery(sql.toString(), MarkLibrary.class);
        Page<MarkLibrary> page = new PageImpl<>(q.getResultList(),
                new PageRequest(query.getPageNumber() - 1, query.getPageSize()), totalCount);
        fillResult(page, query);
        return query;
    }
}
