package it.mice.voila.runtime.security;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

public abstract class AbstractFilterSecurityInterceptorPostProcessor implements
		BeanPostProcessor {
	private static Log logger = LogFactory
			.getLog(ReloadableMethodSecurityMetadataSource.class);

	private String applicationId;
	private Map<String, String> urlsBefore = new HashMap<String, String>();
	private Map<String, String> urlsAfter = new HashMap<String, String>();
	private FilterSecurityInterceptor filterSecurityInterceptor;

	public String getApplicationId() {
		return applicationId;
	}

	public void setApplicationId(String applicationId) {
		this.applicationId = applicationId;
	}

	public Map<String, String> getUrlsBefore() {
		return urlsBefore;
	}

	public void setUrlsBefore(Map<String, String> urlsBefore) {
		this.urlsBefore = urlsBefore;
	}

	public Map<String, String> getUrlsAfter() {
		return urlsAfter;
	}

	public void setUrlsAfter(Map<String, String> urlsAfter) {
		this.urlsAfter = urlsAfter;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	public FilterSecurityInterceptor getFilterSecurityInterceptor() {
		return filterSecurityInterceptor;
	}

	public void setFilterSecurityInterceptor(
			FilterSecurityInterceptor filterSecurityInterceptor) {
		this.filterSecurityInterceptor = filterSecurityInterceptor;
	}

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof FilterSecurityInterceptor) {
			if (logger.isDebugEnabled()) {
				logger.debug("Post-processing bean: " + beanName);
			}
			LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = getRequestMapFromSiteMap();
			if (logger.isDebugEnabled()) {
				logger.debug("Found existing configuration, now iterating.");
				for (ConfigAttribute attribute : ((FilterInvocationSecurityMetadataSource) ((FilterSecurityInterceptor) bean)
						.getSecurityMetadataSource()).getAllConfigAttributes()) {
					logger.debug("*** Attribute: " + attribute.getAttribute());
				}
			}
			((FilterSecurityInterceptor) bean)
					.setSecurityMetadataSource(new DefaultFilterInvocationSecurityMetadataSource(requestMap));
			setFilterSecurityInterceptor((FilterSecurityInterceptor) bean);
		}
		return bean;
	}

	/**
	 * Reload information from the database.
	 */
	public void reloadInformation() {
		try {
			postProcessBeforeInitialization(getFilterSecurityInterceptor(), getFilterSecurityInterceptor().toString());
			getFilterSecurityInterceptor().afterPropertiesSet();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	protected LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> getRequestMapFromSiteMap() {
		LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();

		Iterator<Entry<String, String>> i = urlsBefore.entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry<java.lang.String, java.lang.String> entry = i.next();
			requestMap.put(new AntPathRequestMatcher(entry.getKey()), SecurityConfig.createList(entry.getValue()));
		}
		
		if (logger.isInfoEnabled()) {
			logger.info("Reloading objectDefinitionSource from data base");
		}
		
		populateRequestMapFromSiteMap(requestMap);
		
		i = urlsAfter.entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry<java.lang.String, java.lang.String> entry = i.next();
			requestMap.put(new AntPathRequestMatcher(entry.getKey()), SecurityConfig.createList(entry.getValue()));
		}

		if (logger.isInfoEnabled()) {
			logger.info("Reloading objectDefinitionSource from Database completed");
		}
		
		return requestMap;
	}

	protected abstract void populateRequestMapFromSiteMap(Map<RequestMatcher, Collection<ConfigAttribute>> requestMap);

}
