/**
 * Copyright (c) 2000-2017 Liferay, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Liferay Enterprise
 * pSubscription License ("License"). You may not use this file except in
 * compliance with the License. You can obtain a copy of the License by
 * contacting Liferay, Inc. See the License for the specific language governing
 * permissions and limitations under the License, including but not limited to
 * distribution rights of the Software.
 *
 *
 *
 */

package com.liferay.portal.kernel.search;

import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.search.facet.Facet;
import com.liferay.portal.kernel.search.facet.FacetPostProcessor;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.Time;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.security.permission.ActionKeys;
import com.liferay.portal.security.permission.PermissionChecker;

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

/**
 * @author Tina Tian
 */
public class DefaultSearchResultPermissionFilter
	extends BaseSearchResultPermissionFilter {

	public DefaultSearchResultPermissionFilter() {
	}

	public DefaultSearchResultPermissionFilter(
		BaseIndexer baseIndexer, PermissionChecker permissionChecker) {

		_baseIndexer = baseIndexer;
		_permissionChecker = permissionChecker;
	}

	@Override
	protected void filterHits(Hits hits, SearchContext searchContext) {
		List<Document> docs = new ArrayList<Document>();
		List<Float> scores = new ArrayList<Float>();

		Document[] documents = hits.getDocs();

		List<Document> excludeDocs = new ArrayList<Document>();

		int status = GetterUtil.getInteger(
			searchContext.getAttribute(Field.STATUS),
			WorkflowConstants.STATUS_APPROVED);

		for (int i = 0; i < documents.length; i++) {
			if (_isIncludeDocument(documents[i], status)) {
				docs.add(documents[i]);
				scores.add(hits.score(i));
			}
			else {
				excludeDocs.add(documents[i]);
			}
		}

		if (!excludeDocs.isEmpty()) {
			FacetPostProcessor facetPostProcessor = _facetPostProcessor;

			if (facetPostProcessor != null) {
				Map<String, Facet> facets = searchContext.getFacets();

				for (Facet facet : facets.values()) {
					facetPostProcessor.exclude(excludeDocs, facet);
				}
			}
		}

		hits.setDocs(docs.toArray(new Document[docs.size()]));
		hits.setScores(ArrayUtil.toFloatArray(scores));
		hits.setSearchTime(
			(float)(System.currentTimeMillis() - hits.getStart()) /
				Time.SECOND);
		hits.setLength(hits.getLength() - excludeDocs.size());
	}

	@Override
	protected Hits getHits(SearchContext searchContext) throws SearchException {
		return _baseIndexer.doSearch(searchContext);
	}

	@Override
	protected boolean isGroupAdmin(SearchContext searchContext) {
		if (_permissionChecker.isCompanyAdmin(searchContext.getCompanyId())) {
			return true;
		}

		long groupId = GetterUtil.getLong(
			searchContext.getAttribute(Field.GROUP_ID));

		if (groupId == 0) {
			return false;
		}

		if (!_permissionChecker.isGroupAdmin(groupId)) {
			return false;
		}

		return true;
	}

	private boolean _isIncludeDocument(Document document, int status) {
		String entryClassName = document.get(Field.ENTRY_CLASS_NAME);

		Indexer indexer = IndexerRegistryUtil.getIndexer(entryClassName);

		if (indexer == null) {
			return true;
		}

		if (!indexer.isFilterSearch() || !indexer.isPermissionAware()) {
			return true;
		}

		long entryClassPK = GetterUtil.getLong(
			document.get(Field.ENTRY_CLASS_PK));

		try {
			if (indexer.hasPermission(
					_permissionChecker, entryClassName, entryClassPK,
					ActionKeys.VIEW) &&
				indexer.isVisibleRelatedEntry(entryClassPK, status)) {

				return true;
			}
		}
		catch (Exception e) {
			if (_log.isDebugEnabled()) {
				_log.debug(e, e);
			}
		}

		return false;
	}

	private static Log _log = LogFactoryUtil.getLog(
		DefaultSearchResultPermissionFilter.class);

	private static volatile FacetPostProcessor _facetPostProcessor;

	public void setFacetPostProcessor(FacetPostProcessor facetPostProcessor) {
		_facetPostProcessor = facetPostProcessor;
	}

	private BaseIndexer _baseIndexer;
	private PermissionChecker _permissionChecker;

}