/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.exoplatform.services.jcr.impl.core.query;

import org.apache.lucene.search.Query;
import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
import org.exoplatform.services.jcr.dataflow.ItemDataConsumer;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.impl.checker.InspectionReport;
import org.exoplatform.services.jcr.impl.core.SessionDataManager;
import org.exoplatform.services.jcr.impl.core.SessionImpl;
import org.exoplatform.services.jcr.impl.core.query.lucene.ChangesHolder;
import org.exoplatform.services.jcr.impl.core.query.lucene.IndexInfos;
import org.exoplatform.services.jcr.impl.core.query.lucene.IndexUpdateMonitor;
import org.exoplatform.services.jcr.impl.core.query.lucene.MultiIndex;
import org.exoplatform.services.jcr.impl.core.query.lucene.QueryHits;

import java.io.IOException;
import java.util.Iterator;
import java.util.Set;

import javax.jcr.RepositoryException;
import javax.jcr.query.InvalidQueryException;

/**
 * Defines an interface for the actual node indexing and query execution.
 * The goal is to allow different implementations based on the persistent
 * manager in use. Some persistent model might allow to execute a query
 * in an optimized manner, e.g. database persistence.
 */
public interface QueryHandler
{

   /**
    * Returns the query handler context that passed in {@link
    * #setContext(QueryHandlerContext)}.
    *
    * @return the query handler context.
    */
   QueryHandlerContext getContext();

   /**
    * Adds a <code>Node</code> to the search index.
    * @param node the NodeState to add.
    * @throws RepositoryException if an error occurs while indexing the node.
    * @throws IOException if an error occurs while adding the node to the index.
    */
   void addNode(NodeData node) throws RepositoryException, IOException;

   /**
    * Deletes the Node with <code>id</code> from the search index.
    * @param id the <code>id</code> of the node to delete.
    * @throws IOException if an error occurs while deleting the node.
    */
   void deleteNode(String id) throws IOException;

   /**
    * Updates the index in an atomic operation. Some nodes may be removed and
    * added again in the same updateNodes() call, which is equivalent to an
    * node update.
    *
    * @param remove Iterator of <code>NodeIds</code> of nodes to delete
    * @param add    Iterator of <code>NodeState</code> instance to add to the
    *               index.
    * @throws RepositoryException if an error occurs while indexing a node.
    * @throws IOException if an error occurs while updating the index.
    */
   void updateNodes(Iterator<String> remove, Iterator<NodeData> add) throws RepositoryException, IOException;

   /**
    * Extracts all the changes and returns them as a {@link ChangesHolder} instance
    * @param remove Iterator of <code>NodeIds</code> of nodes to delete
    * @param add    Iterator of <code>NodeState</code> instance to add to the
    *               index.
    * @return a {@link ChangesHolder} instance that contains all the changes
    */
   ChangesHolder getChanges(Iterator<String> remove, Iterator<NodeData> add);

   /**
    * Applies the given changes to the indes in an atomic operation
    * @param changes the changes to apply
    * @throws RepositoryException if an error occurs while indexing a node.
    * @throws IOException if an error occurs while updating the index.
    */
   void apply(ChangesHolder changes) throws RepositoryException, IOException;

   /**
    * Closes this <code>QueryHandler</code> and frees resources attached
    * to this handler.
    */
   void close();

   /**
    * Sets QueryHandlerContext
    * @param context
    */
   void setContext(QueryHandlerContext context);

   /**
    * 
    * initializes QueryHandler
    * 
    * @param ioMode
    * @throws IOException
    * @throws RepositoryException
    * @throws RepositoryConfigurationException
    */
   void init() throws IOException, RepositoryException, RepositoryConfigurationException;

   /**
    * Checks whether QueryHandler is initialized or not
    * 
    * @return
    */
   boolean isInitialized();

   /**
    * Creates a new query by specifying the query statement itself and the
    * language in which the query is stated.  If the query statement is
    * syntactically invalid, given the language specified, an
    * InvalidQueryException is thrown. <code>language</code> must specify a query language
    * string from among those returned by QueryManager.getSupportedQueryLanguages(); if it is not
    * then an <code>InvalidQueryException</code> is thrown.
    *
    * @param session the session of the current user creating the query object.
    * @param itemMgr the item manager of the current user.
    * @param statement the query statement.
    * @param language the syntax of the query statement.
    * @throws InvalidQueryException if statement is invalid or language is unsupported.
    * @return A <code>Query</code> object.
    */
   ExecutableQuery createExecutableQuery(SessionImpl session, SessionDataManager itemMgr, String statement,
      String language) throws InvalidQueryException;

   /**
    * Log unindexed changes into error.log
    * 
    * @param removed set of removed node uuids
    * @param added map of added node states and uuids
    * @throws IOException
    */
   void logErrorChanges(Set<String> removed, Set<String> added) throws IOException;

   void setIndexerIoModeHandler(IndexerIoModeHandler handler) throws IOException;

   IndexerIoModeHandler getIndexerIoModeHandler();

   /**
    * @return the name of the query class to use.
    */
   String getQueryClass();

   /**
    * Executes the query on the search index.
    * 
    * @param queryImpl the query impl.
    * @param query the lucene query.
    * @param orderProps name of the properties for sort order.
    * @param orderSpecs the order specs for the sort order properties. <code>true</code> indicates
    *          ascending order, <code>false</code> indicates descending.
    * @return the lucene Hits object.
    * @throws IOException if an error occurs while searching the index.
    */
   QueryHits executeQuery(Query query) throws IOException;

   /**
    * Sets {@link IndexInfos} instance into QueryHandler, which is later passed to {@link MultiIndex}.
    * 
    * @param indexInfos
    */
   void setIndexInfos(IndexInfos indexInfos);

   /**
    * Returns {@link IndexInfos} instance that was set into QueryHandler.
    * @return
    */
   IndexInfos getIndexInfos();

   /**
    * @return the indexUpdateMonitor
    */
   IndexUpdateMonitor getIndexUpdateMonitor();

   /**
    * @param indexUpdateMonitor the indexUpdateMonitor to set
    */
   void setIndexUpdateMonitor(IndexUpdateMonitor indexUpdateMonitor);

   /**
    * Switches index into corresponding ONLINE or OFFLINE mode. Offline mode means that new indexing data is
    * collected but index is guaranteed to be unmodified during offline state. Passing the allowQuery flag, can
    * allow or deny performing queries on index during offline mode. AllowQuery is not used when setting index
    * back online. When dropStaleIndexes is set, indexes present on the moment of switching index offline will be
    * marked as stale and removed on switching it back online.
    * 
    * @param isOnline
    * @param allowQuery
    *          doesn't matter, when switching index to online
    * @param dropStaleIndexes
    *          doesn't matter, when switching index to online
    * @throws IOException
    */
   void setOnline(boolean isOnline, boolean allowQuery, boolean dropStaleIndexes) throws IOException;

   /**
    * Offline mode means that new indexing data is collected but index is guaranteed to be unmodified during
    * offline state.
    * 
    * @return the state of index.
    */
   boolean isOnline();

   /**
    * Check index consistency. Iterator goes through index documents and check, does each document have
    * according jcr-node. If <b>autoRepair</b> is true - all broken index-documents will be reindexed,
    * and documents that do not have corresponding jcr-node will be removed.
    * 
    * @param itemStateManager
    * @param isSystem
    * @param report
    * @throws RepositoryException
    * @throws IOException
    */
   void checkIndex(ItemDataConsumer itemStateManager, boolean isSystem, InspectionReport report)
      throws RepositoryException, IOException;
}
