package com.k_int.discover.search.action;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.k_int.discover.sru.action.AbstractSearchAction;
import com.k_int.discover.sru.service.CollectionHierarchy;
 
/**
 * Class to handle the collation of advanced search information into the necessary 
 * SRU query for the Culture Grid project
 * 
 * @author rpb rich@k-int.com
 * @version 1.0 13.05.09 
 *
 */
public class AdvancedCultureGridSearchAction extends AbstractSearchAction //implements ApplicationContextAware 
{	
	private static Logger log   = Logger.getLogger(AdvancedCultureGridSearchAction.class);
	
	private static final long serialVersionUID             = 1L;
	private final String SRU_PARAM_SEPARATOR               = " AND ";
	private final String SRU_PARAM_OPTION                  = " OR ";
	
	private String[] collectionRefinements                 = null;
	private String[] sourceRefinements                     = null;
	private int numOfCollections                           = 0;
	private String selectedTextMatchCriteria               = "";
	private String[] typeRefinements                       = null;
        private boolean onlyThumbnails                         = true;
	
	private List<CollectionHierarchy> collectionHierarchy  = new ArrayList<CollectionHierarchy>();

	// Variables for refining text query behaviour
	private CriteriaMapper[] matchCriteriaList = { 	new CriteriaMapper("all of these words", "all", false),
                                                        new CriteriaMapper("at least one of these words", "any", true),
							new CriteriaMapper("the exact phrase", "exact", false)};
	
		
	// Variables for refining by type
	private TypeMapper[] resourceTypes         = {	new TypeMapper("image", "Image"), 
											        new TypeMapper("video", "MovingImage"),
											        new TypeMapper("audio", "Sound")};
	
	
	// Variables for refining where to search
	private FieldMapper[] fieldMatchList       = {  new FieldMapper("anywhere", "fullText"),
											        new FieldMapper("in title & Desc.", "titleAndDesc"),
											        new FieldMapper("in title", "dc.title"),
											        new FieldMapper("in description", "dc.description"),
											        new FieldMapper("in subject", "dc.subject"),
											        new FieldMapper("in creator", "creator"),
											        new FieldMapper("in place", "place"),
											        new FieldMapper("in period", "dcterms.temporal")};
	
	private String[] matchFields               = new String[0];
	
    	
	/**
	 * @see com.k_int.discover.sru.action.AbstractSearchAction#internalExecute()
	 */
	@Override
	protected String internalExecute() throws Exception {
		
		log.debug("In the advanced search internal execute");
		

		// Apply the various refinements - order of application is important!
		if ( query != null ) {
			
			query = this.applyExactMatchAndFields(query);
			query = this.applyCollectionRestrictionToQuery(query);
			query = this.applyTypeRestrictionToQuery(query);
                        query = this.applyOnlyThumbnailsToQuery(query);
			query = this.applyFacetSpecificationToQuery(query);
		}
		
		return "success";
	}



	@Override
	protected void internalSetupPage() throws Exception 
	{
		this.collectionHierarchy = this.getDataService().getAllCollectionsInHierarchy("PN");		
	}
	

	public int getNumOfCollections() {
		return this.numOfCollections;
	}
	
	public void setNumOfCollections(int num) {
		this.numOfCollections = num;
	}

	public void setCollectionRefinements(String[] collectionRefinements) {
		this.collectionRefinements = collectionRefinements;
	}
	
	public void setSourceRefinements(String[] sourceRefinements) {
		this.sourceRefinements = sourceRefinements;
	}
	
	public List<CollectionHierarchy> getCollectionHierarchy() {
		return this.collectionHierarchy;
	}
	
	public CriteriaMapper[] getMatchCriteriaList() {
		return this.matchCriteriaList;
	}
	
	public void setSelectedTextMatchCriteria(String value) {
		this.selectedTextMatchCriteria = value;
	}

        public String getSelectedTextMatchCriteria() {
            return this.selectedTextMatchCriteria;
        }
        
	public TypeMapper[] getResourceTypes() {
		return this.resourceTypes;
	}
	
	public void setTypeRefinements(String[] typeRefinements) {
		this.typeRefinements = typeRefinements;
	}

	
	public FieldMapper[] getFieldMatchList() {
		return this.fieldMatchList;
	}
	
	public void setMatchFields(String[] matchFields) {
		this.matchFields = matchFields;
	}

        public void setOnlyThumbnails(boolean onlyThumbnails) {
            this.onlyThumbnails = onlyThumbnails;
        }
	
	
	private String applyCollectionRestrictionToQuery(String query) {
		String returnQuery = query;
		String collectionQuery = "";

		if ( this.collectionRefinements != null && this.collectionRefinements.length != 0) 
		{
			// Collections have been specified to refine by - loop through them and add
			// them to the query - check that they haven't specified all collections though
			// as there's no point adding this to the query if they have
			if ( collectionRefinements.length != this.numOfCollections ) { 
			
				collectionQuery = "(";
			
				for( int ctr = 0; ctr < this.collectionRefinements.length; ctr++ ) {
					collectionQuery = collectionQuery + SRU_PARAM_OPTION + "dcterms.isPartOf adj \"" + collectionRefinements[ctr] + "\"";
				}
			
				collectionQuery = collectionQuery + ")";
				
				if ( "()".equals(collectionQuery) ) {
					// Shouldn't get here as it means there were no collections to refine by, but
					// tidy it up just in case
					collectionQuery = "";
				} else {
					// Remove the leading SRU_PARAM_OPTION that we don't need
					collectionQuery = collectionQuery.replace("(" + SRU_PARAM_OPTION, "(");
				}

				// Combine the collection part with the rest of the query
				returnQuery = returnQuery + SRU_PARAM_SEPARATOR + collectionQuery;
			}
		}

		if ( this.sourceRefinements != null && this.sourceRefinements.length != 0 ) {
			// Sources have been specified to refine by - loop through them and add
			// them to the query
			String sourceQuery = "(";
			
			for(int ctr = 0; ctr < this.sourceRefinements.length; ctr++) {
				sourceQuery = sourceQuery + SRU_PARAM_OPTION + "authority adj \"" + sourceRefinements[ctr] + "\"";
			}
			
			sourceQuery = sourceQuery + ")";
			
			if ( "()".equals(sourceQuery) ) {
				// Shouldn't get here as it means there were no sources, but tidy it
				// up anyway, just in case
				sourceQuery = "";
			} else {
				// Remove the leading SRU_PARAM_OPTION that we don't need
				sourceQuery = sourceQuery.replace("(" + SRU_PARAM_OPTION, "(");
			}
			
			// Combine the query back in
			returnQuery = returnQuery + SRU_PARAM_SEPARATOR + sourceQuery;
		}
		
		log.debug("Returning a query with collection constraints of " + returnQuery);
		return returnQuery;
	}
	
	private String applyExactMatchAndFields(String query) {
		String returnQuery = query;

		String field = this.matchFields[0]; // TODO - fix for multiple queries..
		
		// TODO - all of the below needs fixing for the case where " are in search term.. (and probably other cases too)
		
		if ( !"titleAndDesc".equals(field) ) {
			// Normal field that maps to one entry in solr, etc.
			if ( this.selectedTextMatchCriteria.equals("exact") ) {
				returnQuery = field + " adj \"" + query + "\"";
			} else if ( this.selectedTextMatchCriteria.equals("all") ) {
				returnQuery = field + " all \"" + query + "\"";
			} else if ( this.selectedTextMatchCriteria.equals("any") ) {
				returnQuery = field + " any \"" + query + "\"";
			}
		} else {
			// Special field where we need to look at two places in solr
			if ( this.selectedTextMatchCriteria.equals("exact") ) {
				returnQuery = "dc.title adj \"" + query + "\" " + SRU_PARAM_OPTION + " dc.description adj \"" + query + "\"";
			} else if ( this.selectedTextMatchCriteria.equals("all") ) {
				returnQuery = "dc.title all \"" + query + "\" " + SRU_PARAM_OPTION + " dc.description all \"" + query + "\"";
			} else if ( this.selectedTextMatchCriteria.equals("any") ) {
				returnQuery = "dc.title any \"" + query + "\" " + SRU_PARAM_OPTION + "dc.description any \"" + query + "\"";
			}
		}

		return returnQuery;
	}
	
	private String applyTypeRestrictionToQuery(String query) {
		String returnQuery = query;
		String typeQuery = "";
		
		// Loop through the specified types and add them in (if there are any)
		if ( this.typeRefinements != null && this.typeRefinements.length != 0) {
			// Types have been specified to refine by - loop through them and add
			typeQuery = "(";
			
			for( int ctr = 0; ctr < this.typeRefinements.length; ctr++ ) {
				typeQuery = typeQuery + SRU_PARAM_OPTION + "dcmi.type adj \"" + typeRefinements[ctr] + "\"";
			}

			typeQuery = typeQuery + ")";
				
			if ( "()".equals(typeQuery) ) {
				// Shouldn't get here as it means there were no types to refine by, but
				// tidy it up just in case
				typeQuery = "";
			} else {
				// Remove the leading SRU_PARAM_OPTION that we don't need
				typeQuery = typeQuery.replace("(" + SRU_PARAM_OPTION, "(");
			}

			// Combine the type part with the rest of the query
			returnQuery = returnQuery + SRU_PARAM_SEPARATOR + typeQuery;
		}
		
		log.debug("Returning after possible adding type refinements to the query: " + returnQuery);
		return returnQuery;
	}
	
	private String applyFacetSpecificationToQuery(String query) {
		String returnQuery = query;
		
		//String facetSpec = "&x-facet=true&x-facet.field=dcterms.isPartOf&x-facet.field=dc.subject"
		//	+ "&x-facet.field=dcmi.type&x-facet.field=authority&x-facet.mincount=1";

		String facetSpec = "&x-facet=default";
		returnQuery = returnQuery + facetSpec;
		
		return returnQuery;
		
	}

      	private String applyOnlyThumbnailsToQuery(String query) {
		String returnQuery = query;

                if ( this.onlyThumbnails ) {
                    returnQuery = returnQuery + this.SRU_PARAM_SEPARATOR + "have_thumbnail adj \"true\"";
                }

		return returnQuery;
	}

}
