/**
 * Copyright 2008-2009 DRIVER PROJECT (ICM UW)
 * Original author: Marek Horst
 *
 * 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 eu.dnetlib.data.index.ws.bbq;

import java.io.IOException;
import java.io.StringReader;
import java.security.InvalidParameterException;
import java.util.List;

import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.log4j.Logger;
import org.apache.xerces.parsers.SAXParser;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.z3950.zing.cql.CQLNode;
import org.z3950.zing.cql.CQLParser;

import eu.dnetlib.resultset.impl.builder.IResultSetBuilder;
import eu.dnetlib.data.index.utils.QueryProvider;
import eu.dnetlib.data.index.ws.harv.FeedObjectResultSetIterator;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.resultset.rmi.ResultSetService;
import eu.dnetlib.enabling.tools.JaxwsServiceResolverImpl;
import eu.dnetlib.enabling.tools.ServiceResolver;

/**
 * BBQ mappings container facade.
 * Performs mappings recreation at service startup.
 * Normalizes bbQueries using zing cql library.
 * @author Marek Horst
 * @version 0.7.6
 *
 */
public class BBQMappingsContainerFacade implements IBBQMappingsContainerFacade {

	protected static final Logger log = Logger.getLogger(BBQMappingsContainerFacade.class);
	
	/**
	 * Collection name <-> BBQ mappings container.
	 */
	private IBBQMappingsContainer mappingsContainer;
	
	/**
	 * IS Lookup service.
	 */
	private ISLookUpService lookUpService;
	
	/**
	 * ResultSet builder module.
	 */
	private IResultSetBuilder resultSetBuilder;
	
	/**
	 * Spring init method.
	 * Recreating collection name <-> BBQ mappings.
	 */
	public void init() {
//		TODO can collection be disabled? should I react to changes?
		try {
			recreateBBQMappings();
		} catch (BBQMappingException e) {
			log.error("Exception occured when recreating collection " +
					"mappings!",e);
		}
	}
	
	/**
	 * Recreates CollName <-> BBQ mappings.
	 * @throws BBQMappingException
	 */
	protected void recreateBBQMappings() throws BBQMappingException {
		String query = QueryProvider.getAllCollectionProfiles();
		try {
			W3CEndpointReference resultSetEPR = lookUpService.searchProfile(query);
			
			if (resultSetEPR == null )
				throw new InvalidParameterException(
						"The query result not returned by lookUp service," +
						" resultSetEPR content can not be null:" +
						"(query:"+query+")");
			
			final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
            final ResultSetService resultSetService = 
				serviceResolver.getService(ResultSetService.class, resultSetEPR);
            final String rsId = serviceResolver.getResourceIdentifier(resultSetEPR);
            
            if (rsId == null )
    			throw new InvalidParameterException(
    					"The resultset identifier not extracted from EPR " +
    					resultSetEPR.toString());
            
			int size = resultSetService.getNumberOfElements(rsId);
			if (size==0) {
				log.debug("No collections found in IS");
				return;
			}
			log.debug("got "+size+" collection profiles from RS");
			//GetResultType resultType = new GetResultType();
			List<String> results = resultSetService.getResult(rsId, 1, size, 
					FeedObjectResultSetIterator.RESULT_SET_REQUEST_MODE_WAITING );
			if (results==null || results.isEmpty() || size != results.size())
				throw new BBQMappingException("Bad number of collection profiles retrieved from RS!");

			SAXParser saxParser = new SAXParser();
			CollectionProfileHandler handler = new CollectionProfileHandler();
			saxParser.setContentHandler(handler);
			for (String currentCollectionProf : results) {
				try {
					saxParser.parse(new InputSource(new StringReader(currentCollectionProf)));
					setMapping(handler.getCollectionName(), handler.getBbQuery());
				} catch (SAXException e) {
					log.error("Exception occured when parsing collection profile: " +
							currentCollectionProf, e);
				} catch (IOException e) {
					log.error("Exception occured when parsing collection profile: " +
							currentCollectionProf, e);
				}
			}
			
		//} catch (org.driver.resultset.GenericFaultMessage e) {
		//	throw new BBQMappingException(
		//			"Couldn't retrieve collection profiles for query: "+query, e);
		} catch (Exception e) {
			throw new BBQMappingException(
					"Couldn't retrieve collection profiles for query: "+query, e);
		}
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.data.index.ws.bbq.IBBQMappingsContainer#getCollectionName(java.lang.String)
	 */
	public String getCollectionName(String bbQuery) {
		return mappingsContainer.getCollectionName(normalizeBBQuery(bbQuery));
	}


	/* (non-Javadoc)
	 * @see eu.dnetlib.data.index.ws.bbq.IBBQMappingsContainerFacade#getCollectionName(org.z3950.zing.cql.CQLNode)
	 */
	public String getCollectionName(CQLNode cqlNode) {
		return mappingsContainer.getCollectionName(cqlNode.toCQL());
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.data.index.ws.bbq.IBBQMappingsContainer#removeMapping(java.lang.String)
	 */
	public String removeMapping(String collectionName) {
		return mappingsContainer.removeMapping(
				normalizeCollectionName(collectionName));
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.index.ws.bbq.IBBQMappingsContainer#setMapping(java.lang.String, java.lang.String)
	 */
	public boolean setMapping(String collectionName, String bbQuery) {
		return mappingsContainer.setMapping(
				normalizeCollectionName(collectionName), normalizeBBQuery(bbQuery));
	}

	/**
	 * Normalizes collection name.
	 * @param collectionName
	 * @return normalized collection name
	 */
	protected String normalizeCollectionName(String collectionName) {
		if (collectionName==null)
			return null;
//		normalization is required when using profId as collection name 
//		because it contains == at the end of profId
		return "\"" + collectionName + "\"";
	}
	
	/**
	 * Normalizes BBQuery by passing String query through cql parser.
	 * @param bbQuery
	 * @return normalized BBQuery.
	 */
	protected String normalizeBBQuery(String bbQuery) {
		if (bbQuery==null)
			return null;
		try {
			CQLParser parser = new CQLParser();
			CQLNode cqlNode = parser.parse(bbQuery);
			if (cqlNode==null) {
				log.error("Got null node after parsing to object model " +
						"for query: "+bbQuery);
				return bbQuery;
			} else {
				return cqlNode.toCQL();
			}
		} catch (Exception e) {
			log.error("Couldn't normalize query " + bbQuery, e);
			return bbQuery;
		}
	}
	
	/**
	 * Returns BBQMappingsContainer.
	 * @return BBQMappingsContainer
	 */
	public IBBQMappingsContainer getMappingsContainer() {
		return mappingsContainer;
	}

	/**
	 * Sets BBQMappingsContainer.
	 * @param mappingsContainer
	 */
	public void setMappingsContainer(IBBQMappingsContainer mappingsContainer) {
		this.mappingsContainer = mappingsContainer;
	}
	
	/**
	 * Returns IS Lookup service.
	 * @return IS Lookup service
	 */
	public ISLookUpService getLookUpService() {
		return lookUpService;
	}

	/**
	 * Sets IS Lookup service.
	 * @param lookUpService
	 */
	public void setLookUpService(ISLookUpService lookUpService) {
		this.lookUpService = lookUpService;
	}

	/**
	 * Returns ResultSet builder module.
	 * @return ResultSet builder module
	 */
	public IResultSetBuilder getResultSetBuilder() {
		return resultSetBuilder;
	}

	/**
	 * Sets ResultSet builder module.
	 * @param resultSetBuilder
	 */
	public void setResultSetBuilder(IResultSetBuilder resultSetBuilder) {
		this.resultSetBuilder = resultSetBuilder;
	}
}
