package it.mice.voila.runtime.security;

import it.mice.voila.runtime.util.VelocityContextUtils;

import java.io.StringWriter;
import java.util.Iterator;
import java.util.Map;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;

/**
 * Created by IntelliJ IDEA.
 * User: zzy9v4
 * Date: 8-giu-2006
 * Time: 23.57.35
 * To change this template use File | Settings | File Templates.
 */
public abstract class AbstractVoter {
    private static final Log logger = LogFactory.getLog(AbstractVoter.class);
    //~ Instance fields ================================================================================================
    private VelocityEngine velocityEngine;
    private ApplicationHolder applicationHolder;
    private Map velocityContextProperties;
    private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();

    //~ Methods ========================================================================================================

    public VelocityEngine getVelocityEngine() {
        return velocityEngine;
    }

    public void setVelocityEngine(VelocityEngine velocityEngine) {
        this.velocityEngine = velocityEngine;
    }

    public ApplicationHolder getApplicationHolder() {
        return applicationHolder;
    }

    public void setApplicationHolder(ApplicationHolder applicationHolder) {
        this.applicationHolder = applicationHolder;
    }

    public Map getVelocityContextProperties() {
        return velocityContextProperties;
    }

    public void setVelocityContextProperties(Map velocityContextProperties) {
        this.velocityContextProperties = velocityContextProperties;
    }

    public AuthenticationTrustResolver getAuthenticationTrustResolver() {
		return authenticationTrustResolver;
	}

	protected String evaluateVelocityTemplate(Object object, String template, StringBuffer propertySetters, Authentication authentication, UserProfileGrantedAuthorityImpl currentRole) {
        if (template == null || template.equals(""))
            return null;

        propertySetters.append("#if( ").append(template).append(" )\r\ntrue#else\r\nfalse#end");

        try {
            VelocityContext context = new VelocityContext(VelocityContextUtils.getVelocityContextToolsMap());
            //ctx of type JfActionContext will be loaded only if presente and of correct type
            if (object != null) {
                context.put("ctx", object);
            }
            context.put("user", authentication.getPrincipal());
            context.put("roles", authentication.getAuthorities());
            context.put("currentRole", currentRole);
            populateVelocityContext(context);

            String logString = "AuthRule";
            StringWriter sw = new StringWriter();
            if (getVelocityEngine().evaluate(context, sw, logString, propertySetters.toString())) {
                if (logger.isDebugEnabled())
                    logger.debug("Template value:\n" + propertySetters + "\nWas converted to:" + sw.toString());
                return sw.toString();
            } else {
                logger.error("Failed to parse Velocity content for Authorization Rule field template:" + propertySetters
                        + ". Check content into column 'Authorization Rule' of the table 'Adf Elementary Operation' for  " + object + ". Probably the content is not a valid Velocity script language.");
                logger.error("Error description:" + logString);
            }
        } catch (Exception e) {
            logger.error("Failed to parse Velocity content for Authorization Rule field template:\n" + propertySetters + "\n"
                    + ". Check content into column 'Authorization Rule' of the table 'Adf Elementary Operation' for  " + object + ". Probably the content is not a valid Velocity script language. Error:" + e.getLocalizedMessage());
        }
        return null;
    }

    private void populateVelocityContext(VelocityContext context) {
        if (getVelocityContextProperties() != null) {
            Iterator e = getVelocityContextProperties().entrySet().iterator();
            while (e.hasNext()) {
                Map.Entry entry = (Map.Entry)e.next();
                context.put(entry.getKey().toString(), entry.getValue());
            }
        }
    }

    protected StringBuffer preparePropertySetters(
            UserProfileGrantedAuthorityImpl gai,
            Authentication authentication,
            OperationConfigAttribute castedAttribute,
            OperationPerProfileConfigAttribute operationPerProfileConfigAttribute) {

        StringBuffer propertySetters = new StringBuffer("");
        //First of all collect application level properties
        propertySetters.append(getApplicationHolder() != null ? getApplicationHolder().getApplicationProps() : "");
        //So collect profile level properties that can ovveride application level one
        propertySetters.append(gai.getProfileProps());
        //So collect user level properties that can ovveride the above one
        propertySetters.append(((UserDetailImpl) authentication.getPrincipal()).getUserProperties());
        //So collect user/profile association level properties that can ovveride the above one
        propertySetters.append(gai.getProfilePerUserProps());
        //So collect operation level properties that can ovveride the above one
        if ((castedAttribute != null) && (castedAttribute.getOperationProps()!=null)) {
            propertySetters.append(castedAttribute.getOperationProps());
        }
        //Finally collect operation per profile association level properties that can ovveride the above one
        if (operationPerProfileConfigAttribute != null) {
            propertySetters.append(operationPerProfileConfigAttribute.getOperationProps());
        }
        return propertySetters;
    }

    protected Object extractContext(Object object) {
        Object ctx;
        try {
            ctx = ((MethodInvocation) object).getArguments()[0];
        } catch (Exception e) {
            return null;
        }
//        if (ctx != null && ctx instanceof JfActionContext) {
//            return (JfActionContext)ctx;
//        }
        return null;
    }
}
