package gr.uoa.di.web.utils.collection;

import eu.dnetlib.api.functionality.CollectionService;
import eu.dnetlib.api.functionality.CollectionServiceException;
import eu.dnetlib.domain.functionality.Collection;
import eu.dnetlib.domain.functionality.CollectionSearchCriteria;
import eu.dnetlib.domain.functionality.UserProfile;
import gr.uoa.di.driver.util.ServiceLocator;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;

public class CollectionManager {

	private static Logger logger = Logger.getLogger(CollectionManager.class);
	ServiceLocator<CollectionService> collectionServiceLocator = null;

	public List<Collection> getAllDriverCollections()
			throws CollectionServiceException {

		return collectionServiceLocator.getService().searchCollections(
				new CollectionSearchCriteria());

	}
	
	public void deleteCollection(String collectionId) throws CollectionServiceException {
		this.collectionServiceLocator.getService().deleteCollection(collectionId);
	}

	public Set<String> getRootCollectionIds() throws CollectionServiceException {

		CollectionSearchCriteria criteria = new CollectionSearchCriteria();
		criteria.setRoot(true);

		List<Collection> allRootCollections = collectionServiceLocator
				.getService().searchCollections(criteria);

		Set<String> rootIds = new HashSet<String>();

		for (Collection col : allRootCollections) {

			logger.debug("add root:" + col.getName()
					+ " collection is private?" + col.isPrivate()
					+ "is Visible?" + col.isVisible() + "owner:"
					+ col.getOwner());

			rootIds.add(col.getResourceId());
		}

		return rootIds;

	}

	public List<Collection> getOwnedCollections(String ownerId)
			throws CollectionServiceException {

		CollectionSearchCriteria searchCriteria = new CollectionSearchCriteria();
		searchCriteria.setOwnerId(ownerId);
		List<Collection> collections = collectionServiceLocator.getService()
				.searchCollections(searchCriteria);
		return collections;

	}

	public Collection getUserCollection(String collectionId, String userId)
			throws CollectionManagerException, CollectionServiceException {

		Collection collection = getCollection(collectionId);
		if (collection == null)
			throw new CollectionManagerException(
					"The collection does not exist.");
		if (collection.getOwner().equals(userId))
			return collection;
		else
			throw new CollectionManagerException(
					"You are not allowed to view this collection.");

	}

	public Collection getCollection(String collectionId)
			throws CollectionServiceException {

		return collectionServiceLocator.getService()
				.getCollection(collectionId);
	}

	// for manager mode
	public List<Collection> getSubTree(String collectionId, UserProfile user)
			throws CollectionServiceException {

		List<Collection> allChildrenList = new ArrayList<Collection>();

		if (collectionId.equals("InfoSpace"))
			getAllChildren(null, allChildrenList, user);
		else
			getAllChildren(getCollection(collectionId), allChildrenList, user);

		return allChildrenList;
	}

	// for view-mode
	public List<Collection> getVisibleSubTree(String collectionId,
			UserProfile user) throws CollectionServiceException {

		List<Collection> allChildrenList = new ArrayList<Collection>();

		if (collectionId.equals("InfoSpace"))
			getVisibleChildren(null, allChildrenList, user);
		else
			getVisibleChildren(getCollection(collectionId), allChildrenList,
					user);

		return allChildrenList;

	}

	public List<Collection> getVisibleContainerSubTree(String collectionId,
			UserProfile user) throws CollectionServiceException {

		List<Collection> allChildrenList = new ArrayList<Collection>();

		if (collectionId.equals("InfoSpace"))
			getVisibleContainerChildren(null, allChildrenList, user);
		else
			getVisibleContainerChildren(getCollection(collectionId),
					allChildrenList, user);

		return allChildrenList;

	}

	public void getVisibleChildren(Collection collection,
			List<Collection> allChildrenList, UserProfile user)
			throws CollectionServiceException {

		Set<String> children = new HashSet<String>();
		if (collection == null)
			children.addAll(getRootCollectionIds());
		else
			children = collection.getChildren();

		for (String collectionId : children) {
			Collection child = getCollection(collectionId);
			if (hasReadPermissions(user, child)) {
				if (child.isVisible())
					allChildrenList.add(child);
				else
					getVisibleChildren(child, allChildrenList, user);
			}
		}

	}

	public void getVisibleContainerChildren(Collection collection,
			List<Collection> allChildrenList, UserProfile user)
			throws CollectionServiceException {

		Set<String> children = new HashSet<String>();
		if (collection == null)
			children.addAll(getRootCollectionIds());
		else
			children = collection.getChildren();

		for (String collectionId : children) {
			Collection child = getCollection(collectionId);
			logger.debug("Testing collection:" + child.getName());
			if (child.isContainer()) {
				logger.debug("It is container");
				if (hasReadPermissions(user, child)) {
					logger.debug("READ OK");
					if (child.isVisible()) {
						logger.debug("VISIBLE OK");
						allChildrenList.add(child);
					} else
						getVisibleChildren(child, allChildrenList, user);
				}
			}
		}

	}

	private boolean hasReadPermissions(UserProfile user, Collection collection) {

		if (!collection.isPrivate())
			return true;

		if (user != null) {

			if (collection.getOwner().equals(user.getResourceId()))
				return true;
		}

		return false;

	}

	private boolean hasManagePermissions(UserProfile user, Collection collection) {

		if (user.getIsSuperUser() || user.getIsCollectionManager())
			return true;
		if (collection.getOwner().equals(user.getResourceId()))
			return true;
		return false;

	}

	public void getAllChildren(Collection collection,
			List<Collection> allChildrenList, UserProfile user)
			throws CollectionServiceException {

		Set<String> children = new HashSet<String>();
		if (collection == null)
			children.addAll(getRootCollectionIds());
		else
			children = collection.getChildren();

		for (String collectionId : children) {

			Collection child = getCollection(collectionId);
			if (hasManagePermissions(user, child))
				allChildrenList.add(child);
			else {
				//if the node is public follow children
				if (!child.isPrivate())
					getAllChildren(child, allChildrenList, user);

			}

		}
	}

	private void setNonEditableFields(Collection collection)
			throws CollectionServiceException {

		Collection editedCollection = getCollection(collection.getResourceId());
		collection.setChildren(editedCollection.getChildren());
		collection.setContainer(editedCollection.isContainer());
		collection.setFather(editedCollection.getFather());
		collection.setDocumentCount(editedCollection.getDocumentCount());
		collection.setOwner(editedCollection.getOwner());
		collection.setRetrievalCondition(editedCollection
				.getRetrievalCondition());
		collection.setQuery(editedCollection.getQuery());
		collection
				.setDocumentsInBasket(editedCollection.getDocumentsInBasket());

	}

	public Collection updateCollection(Collection collection)
			throws CollectionServiceException {

		setNonEditableFields(collection);
		logger.debug("VISIBLE:"+collection.isVisible());
		collectionServiceLocator.getService().updateCollection(collection);
		return collection;

	}

	public Collection saveCollection(Collection collection, String ownerId)
			throws CollectionServiceException {

		// printAll(collection);
		collection.setOwner(ownerId);

		if (collection.getFather() != null && collection.getFather().equals("InfoSpace")) {
			logger.debug("New collection is root collection");
			collection.setFather(null);
		}

		logger.debug("Collection QUERY is:" + collection.getQuery());
		if (collection.getQuery() == null) {
			logger.debug("New collection is container collection");
			collection.setContainer(true);
		} else {
			// TODO DIRTY HACK!!! remove after fixing collection service.
			collection.setRetrievalCondition(collection.getQuery());
		}

		String collectionId = collectionServiceLocator.getService()
				.createCollection(collection);
		logger.debug("Collection ID:" + collectionId);
		return getCollection(collectionId);

	}

	public ServiceLocator<CollectionService> getCollectionServiceLocator() {
		return collectionServiceLocator;
	}

	public void setCollectionServiceLocator(
			ServiceLocator<CollectionService> collectionServiceLocator) {
		this.collectionServiceLocator = collectionServiceLocator;
	}

	public int removeFromBasket(List<String> documentIds, String collectionId)
			throws CollectionServiceException {

		Collection collection = getCollection(collectionId);
		logger.debug("Collection:" + collection + "# documents:"
				+ collection.getDocumentsInBasket().size());

		for (String documentId : documentIds) {
			logger.debug("Remove document ID:" + documentId);
			collection.getDocumentsInBasket().remove(documentId);
		}

		logger.debug("Before updating # of documents"
				+ collection.getDocumentsInBasket());
		collectionServiceLocator.getService().updateCollection(collection);

		return collection.getDocumentsInBasket().size();

	}

}
