/*
 * Copyright 2007 The Fornax Project Team, including the original
 * author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sculptor.framework.accessimpl.jpa;

import java.util.Map;

import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.sculptor.framework.domain.Property;


/**
 * <p>
 * Implementation of Access command FindByQueryAccess.
 * </p>
 * <p>
 * Command design pattern.
 * </p>
 */
public abstract class JpaJpqlQueryAccessBase<T,R>
    extends JpaQueryAccessBase<T,R> {

    private String query;
    private Boolean namedQuery;

    private Property<?>[] fetchEager;
    private TypedQuery<Long> resultCountQuery = null;

	public JpaJpqlQueryAccessBase() {
        super();
    }

    @SuppressWarnings("unchecked")
    public JpaJpqlQueryAccessBase(Class<T> type) {
        super(type, (Class<R>) type);
    }

    public JpaJpqlQueryAccessBase(Class<T> type, Class<R> resultType) {
        super(type, resultType);
    }

    protected String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    public void setFetchEager(Property<?>[] fetchEager) {
        this.fetchEager = fetchEager;
    }

    public Property<?>[] getFetchEager() {
        return fetchEager;
    }

    protected TypedQuery<Long> getResultCountQuery() {
		return resultCountQuery;
	}

    protected void setResultCountQuery(TypedQuery<Long> resultCountQuery) {
		this.resultCountQuery = resultCountQuery;
	}

    protected boolean isNamedQuery() {
        if (namedQuery != null) {
            return namedQuery;
        }
        if (query == null) {
            return false;
        }
        return !query.trim().contains(" ");
    }

    public void setNamedQuery(boolean namedQuery) {
        this.namedQuery = namedQuery;
    }

    @Override
    protected TypedQuery<R> prepareTypedQuery(QueryConfig config) {
        if (isNamedQuery()) {
            return getEntityManager().createNamedQuery(query, getResultType());
        } else {
            return getEntityManager().createQuery(query, getResultType());
        }
    }

    @Override
    protected Query prepareUntypedQuery(QueryConfig config) {
        if (isNamedQuery()) {
            return getEntityManager().createNamedQuery(query);
        } else {
            return getEntityManager().createQuery(query);
        }
    }

    @Override
    final protected void prepareOrderBy(QueryConfig config) {
//	    if (config.hasOrders()) {
	        if (config.isSingleResult() || isNamedQuery()) {
		    	if (config.throwExceptionOnConfigurationError()) {
		            throw new QueryConfigException("Query returns a single result or is a named query, 'order by' not allowed.");
		    	}
		    	return;
		    }
	       	if (query.contains("order by")) {
	        	if (config.throwExceptionOnConfigurationError()) {
	                throw new QueryConfigException("Query contains 'order by' already.");
	        	}
	        	return;
	       	}
//	    }
       	prepareOrderBy(query, config);
    }

    protected void prepareOrderBy(String query, QueryConfig config) {
//	   	query += " order by " + config.getOrderBy();
    }

    @Override
    protected void prepareHints(Query query, QueryConfig config) {
        if (!isNamedQuery()) {
            super.prepareHints(query, config);
        }
    }

    @Override
    protected void prepareResultCount(QueryConfig config) {
        if (isNamedQuery()) {
            // try find a named query for counting rows
            if (JpaHelper.findNamedQuery(getType(), query.replace("find", "count")) != null) {
            	resultCountQuery =
                    getEntityManager().createNamedQuery(query.replace("find", "count"), Long.class);
            } else {
                // guess a query for counting rows based on the named query
                resultCountQuery =
                    getEntityManager().createQuery(
                            JpaHelper.createResultCountQuery(
                                    JpaHelper.findNamedQuery(getType(), query).query()), Long.class);
            }
        } else {
            // guess a query for counting rows based on the query string
            resultCountQuery =
                getEntityManager().createQuery(
                        JpaHelper.createResultCountQuery(query), Long.class);
        }
    };

    @Override
    public void executeResultCount() {
        if (resultCountQuery != null) {
            if (getParameters() != null) {
                for (Map.Entry<String, ?> entry : getParameters().entrySet()) {
                    resultCountQuery.setParameter(entry.getKey(), entry.getValue());
                }
            }
            setResultCount(resultCountQuery.getSingleResult());
        }
    }
}