package eu.dnetlib.enabling.aas.secprof;

import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.DualHashBidiMap;
import org.apache.log4j.Logger;

import eu.dnetlib.enabling.aas.rmi.TypedString;
import eu.dnetlib.enabling.aas.service.A2Constants;
import eu.dnetlib.enabling.aas.utils.QueryProvider;
import eu.dnetlib.enabling.aas.utils.ResourceUtils;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;

/**
 * Proxy for retrieving profiles.
 * @author mhorst
 *
 */
public class ProfilesRetrieverProxy implements IProfilesRetrieverProxy {

	
	protected static final Logger log = Logger.getLogger(ProfilesRetrieverProxy.class);
	
	/**
	 * Security Profile provider.
	 */
	ISecurityProfileProvider securityProfileProvider;
	
	/**
	 * IS LookUp service.
	 */
	private ISLookUpService lookUpService;
	
	/**
	 * Principals caching flag.
	 * Introduced as a response to a problem described in #716 trac ticket.
	 * Set to false by default.
	 */
	private boolean cachePrincipals = false;


	/**
	 * Mapping principals to resourceIds (bidirectional).
	 * Should be accessed only by synchronized methods.
	 */
	private BidiMap principalToResourceIdMap = new DualHashBidiMap();

	/*
	 * (non-Javadoc)
	 * 
	 * @see eu.dnetlib.enabling.aas.secprof.IProfilesRetrieverProxy#findSecurityProfile(eu.dnetlib.enabling.aas.service.TypedString,
	 *      eu.dnetlib.enabling.aas.secprof.ProfileIdentifierLocation)
	 */
	public SecurityProfile findSecurityProfile(TypedString principal,
			ProfileIdentifierLocation loc) {
		if (principal == null)
			return null;
		if (loc != null) {
			// that means we need to search for resourceId within other profile
			String resourceId = (String) getFromPrincipalToResourceIdMap(principal);
			if (resourceId != null) {
				return securityProfileProvider.findSecurityProfile(resourceId);
			} else {
				resourceId = findResourceId(principal.getText(), loc);
				if (resourceId != null) {
					putInPrincipalToResourceIdMap(principal, resourceId);
					return securityProfileProvider
							.findSecurityProfile(resourceId);
				} else
					return null;
			}
		} else {
			// we got resourceId in principal and we can search for secProf directly
			log.info("Direct search for SecurityProfile for resourceId: "
					+ principal.getText());
			return securityProfileProvider.findSecurityProfile(principal
					.getText());
		}
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.aas.secprof.IProfilesRetrieverProxy#removeCachedPrincipal(java.lang.String)
	 */
	public TypedString removeCachedPrincipal(String resourceId) {
		if (resourceId!=null) {
			Object res = removeValueFromPrincipalToResourceIdMap(resourceId);
			if (res!=null) {
				return (TypedString) res;
			} else
				return null;
		} else
			return null;
	}
	
	String findResourceId(String principalText, ProfileIdentifierLocation loc) {
		if (principalText==null || loc==null)
			return null;
		
		String query = QueryProvider.buildQuery_new(loc.getProfileKind(), loc.getProfileType(), 
				loc.getPath(), principalText, A2Constants.IS_PROFILE_ID_XPATH);
		
		if (query==null) {
			return null;
		}
		try {
			String xmlResourceId = lookUpService.getResourceProfileByQuery(query);
			return ResourceUtils.convertProfId(xmlResourceId);
		} catch (ISLookUpDocumentNotFoundException e) {
			log.info("Couldn't find profileId in IS for principal: "+principalText);
			return null;
		} catch (ISLookUpException e) {
			log.error("Exception occured when finding profileId by query " +
					"(for principal id: "+principalText+")!",e);
			return null;
		}
	}
	
	protected synchronized Object getFromPrincipalToResourceIdMap(Object key) {
		if (cachePrincipals) {
			return principalToResourceIdMap.get(key);
		} else {
			return null;
		}
	}

	protected synchronized Object removeValueFromPrincipalToResourceIdMap(Object value) {
		if (cachePrincipals) {
			return principalToResourceIdMap.removeValue(value);
		} else {
			return null;
		}
	}
	
	protected synchronized Object putInPrincipalToResourceIdMap(Object key, Object value) {
		if (cachePrincipals) {
			return principalToResourceIdMap.put(key,value);
		} else {
			return null;
		}
	}
	
	public ISecurityProfileProvider getSecurityProfileProvider() {
		return securityProfileProvider;
	}

	public void setSecurityProfileProvider(
			ISecurityProfileProvider securityProfileProvider) {
		this.securityProfileProvider = securityProfileProvider;
	}

	public ISLookUpService getLookUpService() {
		return lookUpService;
	}

	public void setLookUpService(ISLookUpService lookUpService) {
		this.lookUpService = lookUpService;
	}
	
	/**
	 * Sets cachePrincipals flag.
	 * @param cachePrincipals
	 */
	public void setCachePrincipals(boolean cachePrincipals) {
		this.cachePrincipals = cachePrincipals;
	}
}
