/**
 * 
 */
package kr.co.kihyun.beans.entity.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.jdo.Extent;
import javax.jdo.FetchPlan;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author bhs
 * 
 */
public class QueryImpl implements Query {

	/**
	 * 
	 */
	private static final long serialVersionUID = 2472055604216979420L;
	private static final Logger LOG = LoggerFactory.getLogger(QueryImpl.class);

	private Query q;
	private String filter = "";
	private StringBuilder searchFilter;
	private String declare = "";
	private StringBuilder searchFilterDeclare;
	private SortedMap<String, Object> searchFilterMap = new TreeMap<String, Object>();

	public QueryImpl(Query query) {
		q = query;
	}

	public QueryImpl(PersistenceManager pm) {
		q = pm.newQuery();
	}

	public QueryImpl(PersistenceManager pm, Class<?> cls) {
		q = pm.newQuery(cls);
	}

	public QueryImpl(PersistenceManager pm, Class<?> cls, Collection<?> cln) {
		q = pm.newQuery(cls, cln);
	}

	public QueryImpl(PersistenceManager pm, Class<?> cls, Collection<?> cln, String filter) {
		q = pm.newQuery(cls, cln, filter);
	}

	public QueryImpl(PersistenceManager pm, Class<?> cls, String filter) {
		q = pm.newQuery(cls, filter);
	}

	public QueryImpl(PersistenceManager pm, Extent<?> cln) {
		q = pm.newQuery(cln);
	}

	public QueryImpl(PersistenceManager pm, Extent<?> cln, String filter) {
		q = pm.newQuery(cln, filter);
	}

	public QueryImpl(PersistenceManager pm, Object compiled) {
		q = pm.newQuery(compiled);
	}

	public QueryImpl(PersistenceManager pm, String query) {
		q = pm.newQuery(query);
	}

	public QueryImpl(PersistenceManager pm, String language, Object query) {
		q = pm.newQuery(language, query);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#addExtension(java.lang.String, java.lang.Object)
	 */
	public void addExtension(String arg0, Object arg1) {
		q.addExtension(arg0, arg1);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#addSubquery(javax.jdo.Query, java.lang.String, java.lang.String)
	 */
	public void addSubquery(Query arg0, String arg1, String arg2) {
		q.addSubquery(arg0, arg1, arg2);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#addSubquery(javax.jdo.Query, java.lang.String, java.lang.String, java.lang.String)
	 */
	public void addSubquery(Query arg0, String arg1, String arg2, String arg3) {
		q.addSubquery(arg0, arg1, arg2, arg3);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#addSubquery(javax.jdo.Query, java.lang.String, java.lang.String, java.lang.String[])
	 */
	public void addSubquery(Query arg0, String arg1, String arg2, String... arg3) {
		q.addSubquery(arg0, arg1, arg2, arg3);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#addSubquery(javax.jdo.Query, java.lang.String, java.lang.String, java.util.Map)
	 */
	public void addSubquery(Query arg0, String arg1, String arg2, @SuppressWarnings("rawtypes") Map arg3) {
		q.addSubquery(arg0, arg1, arg2, arg3);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#cancel(java.lang.Thread)
	 */
	public void cancel(Thread arg0) {
		q.cancel(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#cancelAll()
	 */
	public void cancelAll() {
		q.cancelAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#close(java.lang.Object)
	 */
	public void close(Object arg0) {
		q.close(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#closeAll()
	 */
	public void closeAll() {
		q.closeAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#compile()
	 */
	public void compile() {
		q.compile();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#declareImports(java.lang.String)
	 */
	public void declareImports(String arg0) {
		q.declareImports(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#declareParameters(java.lang.String)
	 */
	public void declareParameters(String arg0) {
		this.declare = arg0;
	}

	private void declareParameters() {
		StringBuilder declare = new StringBuilder(this.declare);
		if (searchFilterDeclare != null && searchFilterDeclare.length() > 0) { // if search filter is not empty
			if (this.declare != null && this.declare.length() > 0)
				declare.append(", ");
			declare.append(searchFilterDeclare.toString());
		}
		if (declare.length() > 0)
			q.declareParameters(declare.toString());
		//LOG.debug("declare: {}", declare.toString());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#declareVariables(java.lang.String)
	 */
	public void declareVariables(String arg0) {
		q.declareVariables(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#deletePersistentAll()
	 */
	public long deletePersistentAll() {
		return q.deletePersistentAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#deletePersistentAll(java.lang.Object[])
	 */
	public long deletePersistentAll(Object... arg0) {
		return q.deletePersistentAll(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#deletePersistentAll(java.util.Map)
	 */
	public long deletePersistentAll(@SuppressWarnings("rawtypes") Map arg0) {
		return q.deletePersistentAll(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#execute()
	 */
	public Object execute() {
		this.setFilter();
		this.declareParameters();
		//LOG.debug("param values: {}", searchFilterMap.toString());
		return q.executeWithMap(searchFilterMap);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#execute(java.lang.Object)
	 */
	public Object execute(Object arg0) {
		this.setFilter();
		this.declareParameters();
		List<Object> paramValueList = new ArrayList<Object>();
		paramValueList.add(arg0);
		paramValueList.addAll(searchFilterMap.values());
		//LOG.debug("param values: {}", paramValueList.toString());
		return q.executeWithArray(paramValueList.toArray());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#execute(java.lang.Object, java.lang.Object)
	 */
	public Object execute(Object arg0, Object arg1) {
		this.setFilter();
		this.declareParameters();
		List<Object> paramValueList = new ArrayList<Object>();
		paramValueList.add(arg0);
		paramValueList.add(arg1);
		paramValueList.addAll(searchFilterMap.values());
		//LOG.debug("param values: {}", paramValueList.toString());
		return q.executeWithArray(paramValueList.toArray());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#execute(java.lang.Object, java.lang.Object, java.lang.Object)
	 */
	public Object execute(Object arg0, Object arg1, Object arg2) {
		this.setFilter();
		this.declareParameters();
		List<Object> paramValueList = new ArrayList<Object>();
		paramValueList.add(arg0);
		paramValueList.add(arg1);
		paramValueList.add(arg2);
		paramValueList.addAll(searchFilterMap.values());
		//LOG.debug("param values: {}", paramValueList.toString());
		return q.executeWithArray(paramValueList.toArray());
	}

        public Object execute(Object arg0, Object arg1, Object arg2, Object arg3) {
		this.setFilter();
		this.declareParameters();
		List<Object> paramValueList = new ArrayList<Object>();
		paramValueList.add(arg0);
		paramValueList.add(arg1);
		paramValueList.add(arg2);
                paramValueList.add(arg3);
		paramValueList.addAll(searchFilterMap.values());
		//LOG.debug("param values: {}", paramValueList.toString());
		return q.executeWithArray(paramValueList.toArray());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#executeWithArray(java.lang.Object[])
	 */
	public Object executeWithArray(Object... arg0) {
		this.setFilter();
		this.declareParameters();
		List<Object> paramValueList = new ArrayList<Object>();
		paramValueList.addAll(Arrays.asList(arg0));
		paramValueList.addAll(searchFilterMap.values());
		//LOG.debug("param values: {}", paramValueList.toString());
		return q.executeWithArray(paramValueList.toArray());
                
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#executeWithMap(java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	public Object executeWithMap(@SuppressWarnings("rawtypes") Map arg0) {
		this.setFilter();
		this.declareParameters();
		arg0.putAll(searchFilterMap);
		return q.executeWithMap(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#getDatastoreReadTimeoutMillis()
	 */
	public Integer getDatastoreReadTimeoutMillis() {
		return q.getDatastoreReadTimeoutMillis();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#getDatastoreWriteTimeoutMillis()
	 */
	public Integer getDatastoreWriteTimeoutMillis() {
		return q.getDatastoreWriteTimeoutMillis();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#getFetchPlan()
	 */
	public FetchPlan getFetchPlan() {
		return q.getFetchPlan();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#getIgnoreCache()
	 */
	public boolean getIgnoreCache() {
		return q.getIgnoreCache();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#getPersistenceManager()
	 */
	public PersistenceManager getPersistenceManager() {
		return this.getPersistenceManager();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#getSerializeRead()
	 */
	public Boolean getSerializeRead() {
		return q.getSerializeRead();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#isUnmodifiable()
	 */
	public boolean isUnmodifiable() {
		return q.isUnmodifiable();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setCandidates(javax.jdo.Extent)
	 */
	public void setCandidates(@SuppressWarnings("rawtypes") Extent arg0) {
		q.setCandidates(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setCandidates(java.util.Collection)
	 */
	public void setCandidates(@SuppressWarnings("rawtypes") Collection arg0) {
		q.setCandidates(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setClass(java.lang.Class)
	 */
	public void setClass(@SuppressWarnings("rawtypes") Class arg0) {
		q.setClass(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setDatastoreReadTimeoutMillis(java.lang.Integer)
	 */
	public void setDatastoreReadTimeoutMillis(Integer arg0) {
		q.setDatastoreReadTimeoutMillis(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setDatastoreWriteTimeoutMillis(java.lang.Integer)
	 */
	public void setDatastoreWriteTimeoutMillis(Integer arg0) {
		q.setDatastoreWriteTimeoutMillis(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setExtensions(java.util.Map)
	 */
	public void setExtensions(@SuppressWarnings("rawtypes") Map arg0) {
		q.setExtensions(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setFilter(java.lang.String)
	 */
	public void setFilter(String arg0) {
		this.filter = arg0;
	}

	private void setFilter() {
		StringBuilder filter = new StringBuilder(this.filter);
		if (searchFilter != null && searchFilter.length() > "()".length()) { // if search filter is not empty
			if (this.filter != null && this.filter.length() > 0)
				filter.append(" && ");
			filter.append(searchFilter.toString());
		}
		if (filter.length() > 0)
			q.setFilter(filter.toString());
		//LOG.debug("filter: {}", filter.toString());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setGrouping(java.lang.String)
	 */
	public void setGrouping(String arg0) {
		q.setGrouping(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setIgnoreCache(boolean)
	 */
	public void setIgnoreCache(boolean arg0) {
		q.setIgnoreCache(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setOrdering(java.lang.String)
	 */
	public void setOrdering(String arg0) {
		q.setOrdering(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setRange(java.lang.String)
	 */
	public void setRange(String arg0) {
		q.setRange(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setRange(long, long)
	 */
	public void setRange(long arg0, long arg1) {
		q.setRange(arg0, arg1);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setResult(java.lang.String)
	 */
	public void setResult(String arg0) {
		q.setResult(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setResultClass(java.lang.Class)
	 */
	public void setResultClass(@SuppressWarnings("rawtypes") Class arg0) {
		q.setResultClass(arg0);
	}

	/**
	 * @param filterKey
	 * @param filterValue
	 */
	public void setSearchFilter(String filterKey, Object filterValue) {
		Map<String, Object> filterMap = new HashMap<String, Object>(1);
		filterMap.put(filterKey, filterValue);
		this.setSearchFilter(filterMap);
	}

	/**
	 * @param filterMap
	 */
	public void setSearchFilter(Map<String, Object> filterMap) {
		if (filterMap == null || filterMap.isEmpty())
			return;

		searchFilterMap = new TreeMap<String, Object>();
		searchFilter = new StringBuilder(300);
		searchFilterDeclare = new StringBuilder(300);
		searchFilter.append("(");
		for (Iterator<String> keyIterator = filterMap.keySet().iterator(); keyIterator.hasNext();) {
			String filterKey = keyIterator.next();
			Object filterValue = filterMap.get(filterKey);
			if (filterKey == null || filterKey.length() <= 0)
				continue;
if(filterKey.equals("totDoc.mUser.name")) continue;
if(filterKey.equals("totDoc.mUser.name2")) continue;
			if (filterValue instanceof String) {
				searchFilter.append(filterKey).append(".matches(\".*").append(filterValue).append(".*\")");
			} else {
				if (filterValue instanceof Date) {
					searchFilter.append(filterKey).append(".getYear() == ").append("filter_")
							.append(searchFilterMap.size()).append(".getYear()").append(" && ").append(filterKey)
							.append(".getMonth() == ").append("filter_").append(searchFilterMap.size())
							.append(".getMonth()").append(" && ").append(filterKey).append(".getDay() == ")
							.append("filter_").append(searchFilterMap.size()).append(".getDay()");
				} else {
					searchFilter.append(filterKey).append(" == ").append("filter_").append(searchFilterMap.size());
				}
				searchFilterDeclare.append(filterValue.getClass().getCanonicalName()).append(" ").append("filter_")
						.append(searchFilterMap.size());

				searchFilterMap.put("filter_" + searchFilterMap.size(), filterValue);
			}
			if (keyIterator.hasNext()) {
				searchFilter.append(" && ");
				searchFilterDeclare.append(", ");
			}
		}
		searchFilter.append(")");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setSerializeRead(java.lang.Boolean)
	 */
	public void setSerializeRead(Boolean arg0) {
		q.setSerializeRead(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setUnique(boolean)
	 */
	public void setUnique(boolean arg0) {
		q.setUnique(arg0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jdo.Query#setUnmodifiable()
	 */
	public void setUnmodifiable() {
		q.setUnmodifiable();
	}

}
