package pl.openrest.filters.repository;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.query.Jpa21Utils;
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.data.jpa.repository.support.CrudMethodMetadata;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.QueryDslJpaRepository;
import org.springframework.data.jpa.repository.support.Querydsl;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.SimpleEntityPathResolver;

import pl.openrest.filters.query.PredicateContext;
import pl.openrest.filters.query.registry.JoinInformation;

import com.mysema.query.jpa.JPQLQuery;
import com.mysema.query.jpa.impl.AbstractJPAQuery;
import com.mysema.query.jpa.impl.JPAQuery;
import com.mysema.query.types.EntityPath;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.path.CollectionPath;
import com.mysema.query.types.path.PathBuilder;
import com.mysema.query.types.path.PathBuilderFactory;

public class PredicateContextQueryDslRepositoryImpl<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID> implements
        PredicateContextQueryDslRepository<T> {

    private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
    private final EntityPath<T> path;
    private final PathBuilder<T> builder;
    private final Querydsl querydsl;
    private final EntityManager em;
    private final PathBuilderFactory pathBuilderFactory = new PathBuilderFactory();

    public PredicateContextQueryDslRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
        this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
    }

    public PredicateContextQueryDslRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager,
            EntityPathResolver resolver) {
        super(entityInformation, entityManager);
        this.em = entityManager;
        this.path = resolver.createPath(entityInformation.getJavaType());
        this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
    }

    @Override
    protected JPQLQuery createQuery(Predicate... predicate) {
        return super.createQuery(predicate);
    }

    @Override
    public T findOne(PredicateContext predicateContext) {
        return createQuery(predicateContext).uniqueResult(path);
    }

    @Override
    public Iterable<T> findAll(PredicateContext predicateContext) {
        return createQuery(predicateContext).list(path);
    }

    @Override
    public Iterable<T> findAll(PredicateContext predicateContext, Sort sort) {
        JPQLQuery query = createQuery(predicateContext);
        query = querydsl.applySorting(sort, query);
        return query.list(path);
    }

    @Override
    public Page<T> findAll(PredicateContext predicateContext, Pageable pageable) {
        JPQLQuery countQuery = createQuery(predicateContext);
        JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicateContext));

        Long total = countQuery.count();
        List<T> content = total > pageable.getOffset() ? query.list(path) : Collections.<T> emptyList();
        return new PageImpl<T>(content, pageable, total);
    }

    @Override
    public long count(PredicateContext predicateContext) {
        return createQuery(predicateContext).count();
    }

    protected AbstractJPAQuery<JPAQuery> addJoins(AbstractJPAQuery<JPAQuery> query, PredicateContext context) {
        for (JoinInformation join : context.getJoins()) {
            if (join.isCollection())
                query = query.leftJoin((CollectionPath) join.getPath(), pathBuilderFactory.create(join.getType()));
            else
                query = query.leftJoin((EntityPath) join.getPath());
            if (join.isFetch())
                query = query.fetch();
        }
        return query;
    }

    protected JPQLQuery createQuery(PredicateContext context) {
        AbstractJPAQuery<JPAQuery> query = querydsl.createQuery(path);
        query = addJoins(query, context);
        query.where(context.getPredicate());

        CrudMethodMetadata metadata = getRepositoryMethodMetadata();

        if (metadata == null) {
            return query;
        }

        LockModeType type = metadata.getLockModeType();
        query = type == null ? query : query.setLockMode(type);

        for (Entry<String, Object> hint : metadata.getQueryHints().entrySet()) {
            query.setHint(hint.getKey(), hint.getValue());
        }

        JpaEntityGraph jpaEntityGraph = metadata.getEntityGraph();

        if (jpaEntityGraph == null) {
            return query;
        }

        EntityGraph<?> entityGraph = Jpa21Utils.tryGetFetchGraph(em, jpaEntityGraph);

        if (entityGraph == null) {
            return query;
        }

        query.setHint(jpaEntityGraph.getType().getKey(), entityGraph);

        return query;
    }
}

@@ -149,46 +149,8 @@
 y;%0A%0A
-import javax.persistence.EntityGraph;%0A
 impo
@@ -417,142 +417,8 @@
 rt;%0A
-import org.springframework.data.jpa.repository.query.Jpa21Utils;%0Aimport org.springframework.data.jpa.repository.query.JpaEntityGraph;%0A
 impo
@@ -1723,16 +1723,18 @@
 erydsl;%0A
+//
     priv
@@ -2224,24 +2224,25 @@
 resolver) %7B%0A
+%0A
         supe
@@ -2279,42 +2279,19 @@
 ager
-);%0A        this.em = entityManag
+, resolv
 er
+)
 ;%0A  
@@ -4812,25 +4812,16 @@
  hint : 
-metadata.
 getQuery
@@ -4916,382 +4916,8 @@
  %7D%0A%0A
-        JpaEntityGraph jpaEntityGraph = metadata.getEntityGraph();%0A%0A        if (jpaEntityGraph == null) %7B%0A            return query;%0A        %7D%0A%0A        EntityGraph%3C?%3E entityGraph = Jpa21Utils.tryGetFetchGraph(em, jpaEntityGraph);%0A%0A        if (entityGraph == null) %7B%0A            return query;%0A        %7D%0A%0A        query.setHint(jpaEntityGraph.getType().getKey(), entityGraph);%0A%0A
     

Update PredicateContextQueryDslRepositoryImpl to meet jpa 1.7.3 changes