/**
 * License Agreement.
 *
 *  JBoss RichFaces - Ajax4jsf Component Library
 *
 * Copyright (C) 2007  Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
package org.richfaces.realworld.service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.core.Events;
import org.richfaces.realworld.domain.Image;

@Name("searchService")
@Stateless
public class SearchService implements ISearchService, Serializable
 	{

	private static final long serialVersionUID = -2750591521413940277L;

	@In(value="entityManager")
	EntityManager em;
	@In("#{messages['invalid_syntax']}")
	private String INVALID_SYNTAX;
	@SuppressWarnings("unchecked")
	public List<Image> searchImages(String searchPattern, String additionalParams, Map<String, Object> paramMap){
		List<String> additions = new ArrayList<String>();
		List<String> removals = new ArrayList<String>();
		if(!parse(searchPattern, additions, removals)){
			return null;
		}
		String fullQuery = null;
		fullQuery = setupCaseForQuery(additionalParams, paramMap, fullQuery);
		Query prepared = prepareQuery(fullQuery, additions.get(0), additionalParams, paramMap);
		List<Image> tempResult = prepared.getResultList();
		additions.remove(0);
		return filter(tempResult, additions, removals);
	}

	@SuppressWarnings("unchecked")
	public List<Image> popularImages(){
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_POPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(20);
		return prepared.getResultList();
	}
	
	@SuppressWarnings("unchecked")
	public List<Image> worstImages(){
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_UNPOPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(20);
		return prepared.getResultList();
	}

	@SuppressWarnings("unchecked")
	public List<String> getAllCameras() {
		Query query = em.createNamedQuery(Constants.IMAGE_CAMERA_QUERY);
		return (List<String>)query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public List<String> getAllMetatags() {
		Query query = em.createNamedQuery(Constants.IMAGE_TAGS_QUERY);
		return (List<String>)query.getResultList();
	}
	
	private String setupCaseForQuery(String additionalParams,
			Map<String, Object> paramMap, String fullQuery) {
		if(paramMap != null && paramMap.get(Constants.CASE_SENSITIVE_PARAMETER) != null){
			boolean sensitive = (Boolean)paramMap.get(Constants.CASE_SENSITIVE_PARAMETER);
			if(sensitive){
				fullQuery = Constants.SEARCH_SENSITIVE_QUERY_BEGIN + additionalParams + Constants.SEARCH_QUERY_END;
			}else{
				fullQuery = Constants.SEARCH_QUERY_BEGIN + additionalParams + Constants.SEARCH_QUERY_END;
			}
		}
		return fullQuery;
	}
	
	private Query prepareQuery(String fullQuery ,String searchPattern, String additionalParams,
			Map<String, Object> paramMap) {
		Query prepared = em.createQuery(fullQuery);
		setPattern(searchPattern, paramMap, prepared);
		setParameters(paramMap, prepared);
		prepared.setMaxResults(20);
		return prepared;
	}

	private void setParameters(Map<String, Object> paramMap, Query prepared) {
		if(paramMap != null){
			setParameter(paramMap, prepared, Constants.SPINNER_VALUE_NAMED_PARAMETER, Constants.SPINNER_VALUE_PARAMETER);
			setParameter(paramMap, prepared, Constants.CHOICE_NAMED_PARAMETER, Constants.CHOICE_PARAMETER);
			setParameter(paramMap, prepared, Constants.DATE_NAMED_PARAMETER, Constants.DATE_PARAMETER);
			setParameter(paramMap, prepared, Constants.UPLOAD_NAMED_PARAMETER, Constants.UPLOAD_PARAMETER);
			setParameter(paramMap, prepared, Constants.WIDTH_NAMED_PARAMETER, Constants.WIDTH_PARAMETER);
			setParameter(paramMap, prepared, Constants.HEIGHT_NAMED_PARAMETER, Constants.HEIGHT_PARAMETER);
			setParameter(paramMap, prepared, Constants.SIZE_NAMED_PARAMETER, Constants.SIZE_PARAMETER);
			setParameter(paramMap, prepared, Constants.CAMERA_NAMED_PARAMETER, Constants.CAMERA_PARAMETER);
		}
	}

	private void setParameter(Map<String, Object> paramMap, Query prepared, String parameter, String holder){
		if(paramMap.get(parameter) != null){
			prepared.setParameter(holder, paramMap.get(parameter));
		}
	}
	
	private void setPattern(String searchPattern, Map<String, Object> paramMap,
			Query prepared) {
		if(searchPattern != null){
			if(paramMap != null && paramMap.get(Constants.STRICT_PARAMETER) != null){
				String strict = paramMap.get(Constants.STRICT_PARAMETER).toString();
				if(strict.equals("STRICT")){
					prepared.setParameter(Constants.METATAG_PARAMETER, searchPattern.toUpperCase());
				}else if(strict.equals("START")){
					prepared.setParameter(Constants.METATAG_PARAMETER, searchPattern.toUpperCase()+Constants.PERCENT);
				}else if(strict.equals("END")){
					prepared.setParameter(Constants.METATAG_PARAMETER, Constants.PERCENT + searchPattern.toUpperCase());
				}else if(strict.equals("INCLUDE")){
					prepared.setParameter(Constants.METATAG_PARAMETER, Constants.PERCENT + searchPattern.toUpperCase()+Constants.PERCENT);
				}
			}else{
				prepared.setParameter(Constants.METATAG_PARAMETER, searchPattern.toUpperCase()+Constants.PERCENT);
			}
		}
	}
	
	private List<Image> filter(List<Image> tempResult, List<String> additions,
			List<String> removals) {
		List<Image> result = new ArrayList<Image>();
		/*result.addAll(tempResult);
		for(Image i : tempResult){
			for(String a:additions){
				if(!i.containTag(a)){
					result.remove(i);
					break;
				}
			}
		}
		tempResult.clear();
		tempResult.addAll(result);
		for(Image i : result){
			for(String a:removals){
				if(i.containTag(a)){
					tempResult.remove(i);
					break;
				}
			}
		}*/
		return tempResult;
	}
	
	private boolean parse(String str, List<String> adds, List<String> removes){
		str = str.trim();
		if(str.startsWith(Constants.PLUS) || str.startsWith(Constants.MINUS) || str.endsWith(Constants.PLUS) || str.endsWith(Constants.MINUS) || str.length() == 0){
			Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT, new Exception(INVALID_SYNTAX));
			return false;
		}
		if(str.lastIndexOf(Constants.PLUS_SIGN)==-1 && str.lastIndexOf(Constants.MINUS_SIGN)==-1){
			adds.add(str);
			return true;
		}
		int curIndex=0;
		int prevIndex=0;
		boolean prevSignplus =true;
		boolean signedPreviousChar = false;
		for(int i =0; i< str.length(); i++){
			char c = str.charAt(i);
			if(c == Constants.PLUS_SIGN ){
				if(signedPreviousChar == false){
					
					curIndex = i;
					if(prevSignplus){
						if(prevIndex ==0){
							adds.add(str.substring(prevIndex, curIndex));
						}else{
							adds.add(str.substring(prevIndex+1, curIndex));
						}
					}else{
						removes.add(str.substring(prevIndex+1, curIndex));
					}
					prevSignplus=true;
					signedPreviousChar=true;
					prevIndex =i;
				}else {
					Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT, new Exception(INVALID_SYNTAX));
					return false;
				}
			}else if(c == Constants.MINUS_SIGN){
				if(signedPreviousChar == false){
					curIndex = i;
					signedPreviousChar=true;
					if(prevSignplus){
						if(prevIndex ==0){
							adds.add(str.substring(prevIndex, curIndex));
						}else{
							adds.add(str.substring(prevIndex+1, curIndex));
						}
					}else{
						removes.add(str.substring(prevIndex+1, curIndex));
					}
					prevSignplus=false;
					
					prevIndex =i;
				}else {
					Events.instance().raiseEvent(Constants.ADD_ERROR_EVENT, new Exception(INVALID_SYNTAX));
					return false;
				}
			}else{
				signedPreviousChar=false;
			}
		}
		char c2 = str.charAt(prevIndex);
		if(c2 == Constants.PLUS_SIGN){
			adds.add(str.substring(prevIndex+1));
		}else if(c2 == Constants.MINUS_SIGN){
			removes.add(str.substring(prevIndex+1));
		}
		return true;
	}

	public List<Image> getLastCommentedYourImages() {
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_POPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(5);
		return prepared.getResultList();
	}

	public List<Image> getLastFriendImages() {
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_POPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(5);
		return prepared.getResultList();
	}

	public List<Image> getRecentCommentedImages() {
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_POPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(5);
		return prepared.getResultList();
	}

	public List<Image> getRecentImages() {
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_POPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(5);
		return prepared.getResultList();
	}

	public List<Image> getTopCommentedImages() {
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_POPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(5);
		return prepared.getResultList();
	}

	public List<Image> getTopImages() {
		String fullQuery = Constants.SEARCH_RELEVANT_QUERY_BEGIN + Constants.SEARCH_POPULAR_QUERY_END;
		Query prepared = em.createQuery(fullQuery).setMaxResults(5);
		return prepared.getResultList();
	}
}
