package com.k_int.discover.sru.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.k_int.aggregator.datamodel.AggregatorCollectionTypeHDO;
import com.k_int.aggregator.datamodel.AggregatorCollectionHDO;
import com.k_int.aggregator.datamodel.AggregatorSourceHDO;
import com.k_int.aggregator.dto.AggregatorCollectionDTO;
import com.k_int.aggregator.dto.AggregatorSourceDTO;


public class SearchDataService implements ApplicationContextAware {

	private static final String ALL_SOURCES_FOR_COLL_OQL = "select distinct resource.source from com.k_int.aggregator.datamodel.AggregatorResourceHDO resource join resource.collections as coll where coll.collectionCode = ? order by resource.source.identifier";
	
	private static Logger log   = Logger.getLogger(SearchDataService.class);
	
	private ApplicationContext ctx;
	private SolrServer solrServer;
	
	public void setSolrServer(SolrServer solrServer) { this.solrServer = solrServer; }
	
	/**
	 * Search the database and get a count of the available resources that we're searching over
	 * @return The total number of records over which we're searching
	 */
	public Long getTotalNumOfResourcesFromDB() {
		log.debug("About to get the total number of resources being searched");
		
		Long retval = 0l;
		
		Session sess = null;
		SessionFactory sf = (SessionFactory) ctx.getBean("AggregatorSessionFactory");
		
		try
		{
			sess = sf.openSession();
			Query q = sess.createQuery("Select count(*) from com.k_int.aggregator.datamodel.AggregatorResourceHDO");
			retval = (Long)q.uniqueResult();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if ( sess != null && sess.isOpen() )
					sess.close();
			} catch (Exception ex) {
			
			}
		}
		
		log.debug("Returning num of results as: " + retval);
		
		return retval;
	}
	
	
	public Long getTotalNumOfResourcesFromSolr() {
		Long returnValue = new Long(0);
		
		ModifiableSolrParams params = new ModifiableSolrParams();
		params.set("start", "0");
		params.set("rows","0");
		params.set("q","[* TO *]");
		
		try {
			QueryResponse response = solrServer.query(params);
			
			returnValue = response.getResults().getNumFound();
			
		} catch (SolrServerException sse) {
			log.error("Error occurred getting the total number of resources indexed by solr. Error: " + sse.getMessage());
			sse.printStackTrace();
		}
		
		return returnValue;
	}

	
	public List<CollectionHierarchy> getAllCollectionsInHierarchy() {
		log.debug("getAllCollectionsInHierarchy called");
		
		List<CollectionHierarchy> returnValue = null;

		Session sess = null;
	    SessionFactory sf = (SessionFactory) ctx.getBean("AggregatorSessionFactory");
	    try
	    {
	      sess = sf.openSession();
	    
	      AggregatorCollectionTypeHDO topLevelType = AggregatorCollectionTypeHDO.lookup(sess, "TOP_LEVEL");
	      List<AggregatorCollectionHDO> topLevelColls = AggregatorCollectionHDO.lookupByType(sess, topLevelType);
	      
	      if ( topLevelColls != null ) {
	    	  Iterator<AggregatorCollectionHDO> collIter = topLevelColls.iterator();
	    	  
	    	  while(collIter.hasNext()) {
	    		  AggregatorCollectionHDO aColl = collIter.next();
	    		  if ( aColl.getManagementInfo().getDeletedDate() == null ) {
                              log.debug("no deleted date - adding the collection " + aColl.getCollectionCode() + " to the hierarchy");
	    			  // The collection hasn't been deleted, so include it
	    			  returnValue = this.workoutHierarchy(sess, aColl, 0, false, returnValue);
	    		  } else {
                              log.debug("deleted date for this collection " + aColl.getCollectionCode() + " - not adding the collection to the hierarchy");
                          }
	    	  }
	      }
	      
	    } catch(Exception e) {
	    	e.printStackTrace();
	    } finally {
	    	try
	    	{
	    		if (sess != null && sess.isOpen() )
	    			sess.close();
	    	} catch (Exception ex) {
	    		;
	    	}
	    }

		
		return returnValue;
		
		
	}

	
	public List<CollectionHierarchy> getAllCollectionsInHierarchy(String startCollectionCode) {
		log.debug("getAllCollectionsInHierarchy called with a start collection code of " + startCollectionCode);
		
		List<CollectionHierarchy> returnValue = null;

		Session sess = null;
	    SessionFactory sf = (SessionFactory) ctx.getBean("AggregatorSessionFactory");
	    try
	    {
	      sess = sf.openSession();
	    
	      AggregatorCollectionHDO startColl = AggregatorCollectionHDO.lookup(sess, startCollectionCode);
	      returnValue = this.workoutHierarchy(sess, startColl, 0, false, returnValue);
	      
	    } catch(Exception e) {
	    	e.printStackTrace();
	    } finally {
	    	try
	    	{
	    		if (sess != null && sess.isOpen() )
	    			sess.close();
	    	} catch (Exception ex) {
	    		;
	    	}
	    }

		
		return returnValue;
		
		
	}

	private List<CollectionHierarchy> workoutHierarchy(Session sess, AggregatorCollectionHDO coll, int level, boolean lastChild, List<CollectionHierarchy> listSoFar) {
		
		if ( coll != null ) {
			
			if ( coll.getManagementInfo() != null && coll.getManagementInfo().getSearchable() && coll.getManagementInfo().getDeletedDate() == null ) {
				if ( listSoFar == null ) {
					listSoFar = new ArrayList<CollectionHierarchy>();
				}
				
				boolean hasChildren = false;
				if ( coll.getManagementInfo() != null && coll.getManagementInfo().getSearchShowChildCollections() && coll.getChildrenCollections() != null && coll.getChildrenCollections().size() > 0 ) {
					hasChildren = true;
				}
				
				AggregatorCollectionDTO collDTO = new AggregatorCollectionDTO(coll);
				
				// Get the sources for the collection
				List<AggregatorSourceDTO> sources = null;
	
				if ( collDTO.getManagementInfo() != null && collDTO.getManagementInfo().getSearchShowSources() ) {
					sources = new ArrayList<AggregatorSourceDTO>(); 			 
					
					Query sourcesQuery = sess.createQuery(ALL_SOURCES_FOR_COLL_OQL);
					sourcesQuery.setParameter(0, coll.getCollectionCode());
				  
					@SuppressWarnings("unchecked")
					List<AggregatorSourceHDO> sourcesForColl = (List<AggregatorSourceHDO>)sourcesQuery.list();
				  
					Iterator<AggregatorSourceHDO> sourceIter = sourcesForColl.iterator();
					while(sourceIter.hasNext()) {
						AggregatorSourceHDO thisSource = sourceIter.next();
						if ( thisSource != null ) {
							AggregatorSourceDTO sourceDTO = new AggregatorSourceDTO(thisSource);
							sources.add(sourceDTO);
						}
					}
				}
	
				// Add this collection and its sources to the list
				CollectionHierarchy collHier = new CollectionHierarchy(level, collDTO, hasChildren, lastChild, sources);
				listSoFar.add(collHier);
			
				boolean visibleChild = false;
				
				if ( coll.getManagementInfo() != null && coll.getManagementInfo().getSearchShowChildCollections() ) {
					// Loop through the children (if any) adding them
					List<AggregatorCollectionHDO> children = coll.getChildrenCollections();
					// Sort the list first though so we get things in alphabetical order in the search page(s)
					Collections.sort(children, new CollectionNameComparator());
				
					// Loop through all of the children and create a list that just includes those that are
					// publicly available
					List<AggregatorCollectionHDO> publicChildren = null;
					
					if ( children != null ) {
						publicChildren = new ArrayList<AggregatorCollectionHDO>();
						Iterator<AggregatorCollectionHDO> childIter = children.iterator();
						while(childIter.hasNext()) {
							AggregatorCollectionHDO nextChild = childIter.next();
							if ( nextChild != null && nextChild.getManagementInfo() != null && nextChild.getManagementInfo().getSearchable() && nextChild.getManagementInfo().getDeletedDate() == null ) {
								publicChildren.add(nextChild);
							}
						}
					}
					
					if ( publicChildren != null ) {
						Iterator<AggregatorCollectionHDO> publicChildIter = publicChildren.iterator();
						while(publicChildIter.hasNext()) {
							AggregatorCollectionHDO aChild = publicChildIter.next();
						
							boolean isLastChild = false;
							if ( !publicChildIter.hasNext() )
								isLastChild = true;
						
							if ( aChild != null ) {
								listSoFar = this.workoutHierarchy(sess, aChild, level+1, isLastChild, listSoFar);
								
								if ( aChild.getManagementInfo().getSearchable() ) {
									visibleChild = true;
								}
							}
							
						}
					}
				}
				
				collHier.hasChildren = visibleChild;

                        }
		}
		
		return listSoFar;
	}

	
	public void setApplicationContext(ApplicationContext ctx) {
	    this.ctx = ctx;
	}
	
	public void init() {
		log.debug("Initialising SearchDataService");
	}

	
	public class CollectionNameComparator implements java.util.Comparator<AggregatorCollectionHDO>
	{

		public int compare(AggregatorCollectionHDO coll1, AggregatorCollectionHDO coll2) {
			if ( coll1 != null && coll2 != null && coll1.getName() != null && !"".equals(coll1.getName().trim()) && coll2.getName() != null && !"".equals(coll2.getName().trim()) ) {
				// We can do some comparison
				return coll1.getName().trim().compareToIgnoreCase(coll2.getName().trim());
			}
			return 0;
		}
		
	}
	
	

}

