package eu.dnetlib.enabling.aas.finder.modules;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;


import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.ParsingException;
import com.sun.xacml.UnknownIdentifierException;
import com.sun.xacml.attr.AttributeFactory;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.attr.BagAttribute;
import com.sun.xacml.cond.EvaluationResult;

import eu.dnetlib.enabling.aas.ObligationsResolver;
import eu.dnetlib.enabling.aas.finder.ctx.DriverEvaluationCtx;
import eu.dnetlib.enabling.aas.rmi.A2Request;
import eu.dnetlib.enabling.aas.rmi.AuthenticateRequest;
import eu.dnetlib.enabling.aas.rmi.TypedString;
import eu.dnetlib.enabling.aas.secprof.IProfilesRetrieverProxy;
import eu.dnetlib.enabling.aas.secprof.ProfileIdentifierLocation;
import eu.dnetlib.enabling.aas.secprof.SecurityProfile;
import eu.dnetlib.enabling.aas.service.A2Exception;
import eu.dnetlib.enabling.aas.utils.ResourceUtils;

/**
 * Finder module working on SecurityProfileProvider.
 * @author mhorst
 *
 */
public class SecProfileAttrFM  extends CommonAttrFM implements A2RequestFinderModule{

	protected static final Logger log = Logger.getLogger(SecProfileAttrFM.class);

	public static final String SUPPORTED_PREFIX 	= "security-profile:";
	public static final String SUPPORTED_RESOURCEID_PREFIX 	= SUPPORTED_PREFIX + "resourceId";
	public static final String SUPPORTED_ATTRIBUTE_PREFIX 	= SUPPORTED_PREFIX + "attribute:";
	public static final String SUPPORTED_IDENTITY_PREFIX 	= SUPPORTED_PREFIX + "identity:";

	private IProfilesRetrieverProxy profilesRetrieverProxy;
	
	/**
	 * Returns true always because this module supports designators.
	 *
	 * @return true always
	 */
	@Override
	public boolean isDesignatorSupported() {
		return true;
	}
	
	

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.aas.finder.modules.A2RequestFinderModule#findAttribute(java.net.URI, java.net.URI, java.net.URI, java.net.URI, com.sun.xacml.EvaluationCtx, int, eu.dnetlib.enabling.aas.service.A2Request)
	 */
	public EvaluationResult findAttribute(URI attributeType, URI attributeId,
			URI issuer, URI subjectCategory, EvaluationCtx context,
			int designatorType, A2Request a2Request) {

//		 figure out which attribute we're looking for
		String attrName = attributeId.toString();
		if (!attrName.startsWith(SUPPORTED_PREFIX))
			return new EvaluationResult(BagAttribute
					.createEmptyBag(attributeType));
		
		if (a2Request==null) {
			log.error("A2Request is null! Cannot search for attributes!");
			return new EvaluationResult(BagAttribute
					.createEmptyBag(attributeType));
		}
		
		AuthenticateRequest authenticateRequest = null;
		if (a2Request instanceof AuthenticateRequest)
			authenticateRequest = (AuthenticateRequest) a2Request;
		else {
			log.info("A2Request is not an instance of AuthenticateRequest! " +
					"Cannot search for SecurityProfile attributes!");
			return new EvaluationResult(BagAttribute
					.createEmptyBag(attributeType));
		}

//		checking if FinderModule was called by ObligationsResolver
		URI secProfIssuer = null;
		if (issuer!=null) {
			if (issuer.toString().equals(ObligationsResolver.OBLIGATIONS_RESOLVER_ISSUER)) {
				//retrieving issuer from DriverEvaluationContext
				if (context instanceof DriverEvaluationCtx) {
					DriverEvaluationCtx driverContext = (DriverEvaluationCtx) context;
					secProfIssuer = (URI) driverContext.getEvaluationAttribute(DriverEvaluationCtx.EVAL_ATTR_SECURITY_PROFILE_LOCATION);
					if (secProfIssuer==null)
						log.warn("SecProf location retrieved from DriverEvaluationContext for obligation is null." +
								"Is this SecProf with direct access?");
				} else {
					log.error("Couldn't retrieve issuer URI! Current context is not an instance of DriverEvaluationCtx!");
				}
				
			} else {
				secProfIssuer = issuer;
//				store issuer within DriverEvaluationContext
				if (context instanceof DriverEvaluationCtx) {
					DriverEvaluationCtx driverContext = (DriverEvaluationCtx) context;
					driverContext.setEvaluationAttribute(DriverEvaluationCtx.EVAL_ATTR_SECURITY_PROFILE_LOCATION, issuer);
				} else {
					log.error("Couldn't store issuer URI in DriverEvalutaionContext! " +
							"Current context is not an instance of DriverEvaluationCtx!");
				}
			}
		}

		SecurityProfile secProf = findSecurityProfile(authenticateRequest, secProfIssuer);
		if (secProf==null) {
			return new EvaluationResult(BagAttribute
					.createEmptyBag(attributeType));
		}
		
		try {
			if (attrName.equals(SUPPORTED_RESOURCEID_PREFIX)) {
//				getting resourceId of SecurityProfile
				if (secProf.getResourceId()!=null) {
					AttributeFactory attrFactory = AttributeFactory.getInstance();
					List<AttributeValue> allResults = new ArrayList<AttributeValue>();
					allResults.add(attrFactory.createValue(attributeType, secProf.getResourceId()));
					return new EvaluationResult(new BagAttribute(attributeType,	allResults));
				} else
					return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
				
			} else if (attrName.startsWith(SUPPORTED_IDENTITY_PREFIX)) {
//				searching inside identities
				AttributeFactory attrFactory = AttributeFactory.getInstance();
				String identityName = attrName.substring(SUPPORTED_IDENTITY_PREFIX.length(), attrName.length());
				List<AttributeValue> allResults = findAttributeValues(identityName, attributeType, secProf.getIdentities(), attrFactory);
				if (allResults!=null && allResults.size()>0) {
					return new EvaluationResult(new BagAttribute(attributeType,	allResults));
				} else {
					return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
				}
				
			} else if (attrName.startsWith(SUPPORTED_ATTRIBUTE_PREFIX)){
//				searching inside attributes
				AttributeFactory attrFactory = AttributeFactory.getInstance();
				String attributeName = attrName.substring(SUPPORTED_ATTRIBUTE_PREFIX.length(), attrName.length());
				List<AttributeValue> allResults = findAttributeValues(attributeName, attributeType, secProf.getAttributes(), attrFactory);
				if (allResults!=null && allResults.size()>0) {
					return new EvaluationResult(new BagAttribute(attributeType,	allResults));
				} else {
					return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
				}

			} else {
				log.warn("Unsupported prefix for searching inside security-profile! " +
						"Attribute or identity are supported!");
				return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
			}
			
		} catch (UnknownIdentifierException uie) {
			log.error(
					"UnknownIdentifierException occured while finding attribute value in SecurityProfile!",
					uie);
			return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
		} catch (ParsingException pe) {
			log.error(
					"ParsingException occured while finding attribute value in SecurityProfile!",
					pe);
			return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
		}
	}

	
	/**
	 * Returns SecurityProfile for given AuthenticateRequest as parameter.
	 * @param authenticateRequest
	 * @param identifierLocation
	 * @return SecurityProfile
	 */
	SecurityProfile findSecurityProfile(AuthenticateRequest authenticateRequest, 
			URI identifierLocation) {
		TypedString principal = null;
		try {
			principal = ResourceUtils.findResourceIdPrincipal(authenticateRequest);
		} catch (A2Exception e) {
			log.error("Exception when trying to find principal containing resourceId!",e);
			return null;
		}
		if (principal==null) {
			log.info("No resourceId found within AuthenticateRequest");
			return null;
		}
		ProfileIdentifierLocation loc = parseFieldLocation(identifierLocation);
		SecurityProfile res = profilesRetrieverProxy.findSecurityProfile(principal,loc);
		if (res==null) {
			log.warn("Couldn't find SecurityProfile for resourceId: " + 
					principal.getText() + "!");
		}
		return res;
	}
	
	
	/**
	 * Returns field location. 
	 * @param sourceURI
	 * @return ProfileIdentifierLocation
	 */
	public static ProfileIdentifierLocation parseFieldLocation(URI sourceURI) {
		String fieldsDelimiter = ":";
		if (sourceURI==null)
			return null;
		String source = sourceURI.toString();
		if (source==null)
			return null;
		String[] result =  StringUtils.tokenizeToStringArray(source, fieldsDelimiter);
		if (result.length==3) {
			ProfileIdentifierLocation loc = new ProfileIdentifierLocation();
			loc.setProfileKind(result[0]);
			loc.setProfileType(result[1]);
			loc.setPath(result[2]);
			return loc;
		} else
			return null;
	}


	/**
	 * Returns profilesRetrieverProxy.
	 * @return profilesRetrieverProxy
	 */
	public IProfilesRetrieverProxy getProfilesRetrieverProxy() {
		return profilesRetrieverProxy;
	}

	/**
	 * Sets profilesRetrieverProxy.
	 * @param profilesRetrieverProxy
	 */
	public void setProfilesRetrieverProxy(
			IProfilesRetrieverProxy profilesRetrieverProxy) {
		this.profilesRetrieverProxy = profilesRetrieverProxy;
	}

}
