/*
 * (C) Copyright 2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * 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.
 *
 * Contributors:
 *     Florent Guillaume
 */

package org.nuxeo.ecm.platform.search.backend.core;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.DocumentSecurityException;
import org.nuxeo.ecm.core.api.impl.DocumentModelImpl;
import org.nuxeo.ecm.core.api.impl.UserPrincipal;
import org.nuxeo.ecm.core.api.repository.Repository;
import org.nuxeo.ecm.core.api.repository.RepositoryManager;
import org.nuxeo.ecm.core.query.sql.model.SQLQuery;
import org.nuxeo.ecm.core.search.api.backend.impl.AbstractSearchEngineBackend;
import org.nuxeo.ecm.core.search.api.backend.indexing.resources.ResolvedResources;
import org.nuxeo.ecm.core.search.api.client.IndexingException;
import org.nuxeo.ecm.core.search.api.client.SearchException;
import org.nuxeo.ecm.core.search.api.client.indexing.session.SearchServiceSession;
import org.nuxeo.ecm.core.search.api.client.query.ComposedNXQuery;
import org.nuxeo.ecm.core.search.api.client.query.NativeQuery;
import org.nuxeo.ecm.core.search.api.client.query.NativeQueryString;
import org.nuxeo.ecm.core.search.api.client.query.QueryException;
import org.nuxeo.ecm.core.search.api.client.query.SearchPrincipal;
import org.nuxeo.ecm.core.search.api.client.search.results.ResultItem;
import org.nuxeo.ecm.core.search.api.client.search.results.ResultSet;
import org.nuxeo.ecm.core.search.api.client.search.results.impl.DocumentModelResultItem;
import org.nuxeo.ecm.core.search.api.client.search.results.impl.ResultSetImpl;
import org.nuxeo.runtime.api.Framework;

/**
 * Search engine backend that delegates to the core.
 *
 * @author Florent Guillaume
 *
 */
public class CoreSearchBackend extends AbstractSearchEngineBackend {

    private static final long serialVersionUID = 1L;

    private static final Log log = LogFactory.getLog(CoreSearchBackend.class);

    public SearchServiceSession createSession() {
        // called by IndexingThreadImpl
        throw new UnsupportedOperationException();
    }

    public void closeSession(String sid) {
        // called by IndexingThreadImpl
        throw new UnsupportedOperationException();
    }

    public void saveAllSessions() {
        // ignore save
    }

    public void clear() throws IndexingException {
        throw new UnsupportedOperationException();
    }

    public void index(ResolvedResources resources) {
        // ignore indexing
    }

    public void deleteAggregatedResources(String key) {
        // ignore deletion
    }

    public void deleteAtomicResource(String key) {
        // ignore deletion
    }

    public ResultSet searchQuery(NativeQueryString queryString, int offset,
            int range) throws QueryException {
        throw new QueryException("Native query not implemented");
    }

    public ResultSet searchQuery(NativeQuery nativeQuery, int offset, int range)
            throws QueryException {
        throw new QueryException("Native query not implemented");
    }

    public ResultSet searchQuery(ComposedNXQuery nxQuery, int offset, int limit)
            throws SearchException, QueryException {
        SearchPrincipal searchPrincipal = nxQuery.getSearchPrincipal();
        Serializable principal = getPrincipal(searchPrincipal);

        Repository repository;
        CoreSession session;
        try {
            repository = Framework.getService(RepositoryManager.class).getDefaultRepository();
            if (repository == null) {
                throw new ClientException("Cannot get default repository");
            }
            Map<String, Serializable> context = new HashMap<String, Serializable>();
            context.put("principal", principal);
            session = repository.open(context);
        } catch (Exception e) {
            throw new SearchException(e);
        }
        try {
            return searchQuery(nxQuery.getQuery(), offset, limit, session,
                    searchPrincipal);
        } catch (ClientException e) {
            throw new SearchException(e);
        } finally {
            try {
                repository.close(session);
            } catch (Exception e) {
                throw new SearchException(e);
            }
        }
    }

    protected static ResultSet searchQuery(SQLQuery sqlQuery, int offset,
            int limit, CoreSession session, SearchPrincipal searchPrincipal)
            throws ClientException {
        // TODO record the orignal query in sqlQuery instead of calling toString
        String query = sqlQuery.toString();
        int max = limit == 0 ? 0 : offset + limit;
        DocumentModelList documentModelList = session.query(query, max);
        int size = documentModelList.size();
        int pageHits = size - offset;
        List<ResultItem> resultItems = new ArrayList<ResultItem>(size);
        for (DocumentModel doc : documentModelList) {
            if (doc == null) {
                log.error("Got null document from query: " + query);
                continue;
            }
            // detach the document so that we can use it beyond the session
            try {
                ((DocumentModelImpl) doc).detach(true);
            } catch (DocumentSecurityException e) {
                // no access to the document (why?)
                continue;
            }
            resultItems.add(new DocumentModelResultItem(doc));
        }
        return new ResultSetImpl(sqlQuery, "core", searchPrincipal, offset,
                limit, resultItems, size, pageHits);
    }

    protected static Serializable getPrincipal(SearchPrincipal searchPrincipal) {
        if (searchPrincipal == null) {
            // TODO better way to build a system user
            return new UserPrincipal("system");
        }
        Serializable originalPrincipal = searchPrincipal.getOriginalPrincipal();
        if (originalPrincipal != null) {
            return originalPrincipal;
        }
        return new UserPrincipal(searchPrincipal.getName(),
                Arrays.asList(searchPrincipal.getGroups()));
    }

}

@@ -5341,267 +5341,184 @@
     
-// TODO record the orignal query in sqlQuery instead of calling toString%0A        String query = sqlQuery.toString();%0A        int max = limit == 0 ? 0 : offset + limit;%0A        DocumentModelList documentModelList = session.query(query, max);%0A        int size =
+String query = sqlQuery.toString();%0A        DocumentModelList documentModelList = session.query(query, null, limit,%0A                offset, true);%0A        int totalHits = (int)
  doc
@@ -5532,17 +5532,22 @@
 delList.
-s
+totalS
 ize();%0A 
@@ -5572,21 +5572,32 @@
 s = 
-size - offset
+documentModelList.size()
 ;%0A  
@@ -5659,20 +5659,24 @@
 ltItem%3E(
-size
+pageHits
 );%0A     
@@ -6343,20 +6343,25 @@
 tItems, 
-size
+totalHits
 , pageHi

fix batching in CoreSearchBackend thanks to NXP-3067