/**
 * (c) Richard Unger 2014
 */
package at.lfrz.magnolia.atk.availability;


import java.util.List;

import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.RepositoryException;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import info.magnolia.cms.security.SecuritySupport;
import info.magnolia.cms.security.User;
import info.magnolia.cms.security.UserManager;
import info.magnolia.context.MgnlContext;
import info.magnolia.module.templatingkit.sites.TemplateAvailability;
import info.magnolia.module.templatingkit.templates.pages.STKPage;
import info.magnolia.registry.RegistrationException;
import info.magnolia.rendering.template.TemplateDefinition;
import info.magnolia.rendering.template.assignment.TemplateDefinitionAssignment;

/**
 * <b>AllowedTemplateAvailability</b><br/>
 * <p>
 * Extends the standard mechanism for template availability, using explicitly
 * configured template availability. To use this class, make sure your template definitions
 * are set to use a class that implements {@link AllowedTemplatesConfigurable}.<br/>
 * Then, configure an "allowedTemplates" node in the template definition.
 * Under this node, add one node for each allowed template, with the following properties:
 * "template" -> the definition name of the allowed template (required)
 * "roles" -> the roles for which use of this template is allowed (optional)
 * "groups" -> the groups for which use of this template is allowed (optional)
 * </p>
 * <p>
 * If this availability class is invoked for a given node, the following checks are performed:
 *   - the template definition for the parent node is checked:
 *       - if the parent node has explicitly allowed templates, then the desired template 
 *         is checked against the list of allowed templates.
 *       - if the parent node does not have explicitly allowed templates, the standard
 *         template availability mechanism is used.
 *   - if the parent had configuration for allowed templates:
 *       - if the desired template is not in the list, it is not available
 *       - if the desired template is in the list, and roles or groups are configured, these
 *         are checked
 *       - if the current user has the required roles or groups, the desired template is allowed
 *       - otherwise, the desired template is disallowed
 * </p>
 * @author Richard Unger, (c) 2014
 */
public class AllowedTemplateAvailability extends TemplateAvailability {

	public static final Logger log = LoggerFactory.getLogger(AllowedTemplateAvailability.class);
	
	protected TemplateDefinitionAssignment templateAssignment;
	protected UserManager userManager;


	@Inject
	public AllowedTemplateAvailability(TemplateDefinitionAssignment templateAssignment, SecuritySupport securitySupport){
		this.templateAssignment = templateAssignment;
		this.userManager = securitySupport.getUserManager();
		//Components.getComponent(TemplateDefinitionAssignment.class)
		//Components.getComponent(SecuritySupport.class).getUserManager()
		log.debug("AllowedTemplateAvailability instantiated...");
	}
	
	
	/**
	 * Check template-availability based on parent page template
	 * @param parent the parent page of the page we would like to assign a template
	 * @param template the template we would like to assign
	 * @return true if available, false if forbidden, null if undecided
	 * @throws RepositoryException
	 * @throws RegistrationException 
	 */
	public Boolean isAvailableFromParent(Node parent, STKPage template) throws RepositoryException, RegistrationException{
		String desiredTemplate = template.getId();
		TemplateDefinition t = templateAssignment.getAssignedTemplateDefinition(parent);
		if (t!=null && t instanceof AllowedTemplatesConfigurable){
			log.debug("Checking for "+template.getName()+" from parent template type: "+t.getName());
			AllowedTemplatesConfigurable atc = (AllowedTemplatesConfigurable)t;
			List<AllowedTemplate> theAllowedTemplates = atc.getAllowedTemplates();
			for (AllowedTemplate at : theAllowedTemplates){
				if (desiredTemplate.equals(at.getTemplate())){
					// check if roles or groups were configured
					if (at.getRoles()!=null || at.getGroups()!=null){
						// check roles
						if (at.getRoles()!=null)
							if (currentUserHasAnyOfTheseRoles(at.getRoles()))
								return true;
						// check groups
						if (at.getGroups()!=null)
							if (currentUserHasAnyOfTheseGroups(at.getGroups()))
								return true;
						// if roles or groups was not null, but the user did not have any of them, we return false
						return false;
					}
					// if no roles or groups were configured, we return true
					return true;
				}
			}
			if (theAllowedTemplates==null || theAllowedTemplates.size()==0){
				// if the allowedTemplates node is present, but no templates are configured, return false - then no templates at all are allowed
				return false;
			}
			if (theAllowedTemplates!=null && theAllowedTemplates.size()>0)
				return false;  // if templates were configured, then return false.
		}
		// If no templates were configured, or parent template is not of type AllowedTemplatesConfigurable then result is undecided!
		return null;
	}
	
	
	/**
	 * @param groups comma-separated list of groups
	 * @return true if the user is member of any of the groups, false otherwise or if groups param is empty or null
	 */
	public boolean currentUserHasAnyOfTheseGroups(String groups) {
		if (StringUtils.isEmpty(groups))
			return false;				
		User user = MgnlContext.getUser();
		if (user!=null){
			for (String grp : groups.split(","))
				if (user.inGroup(grp))
					return true;
		}		
		return false;
	}


	/**
	 * @param roles comma-separated list of roles
	 * @return true if the user is member of any of the roles, false otherwise or if roles param is empty or null
	 */
	public boolean currentUserHasAnyOfTheseRoles(String roles) {
		if (StringUtils.isEmpty(roles))
			return false;				
		User user = MgnlContext.getUser();
		if (user!=null){
			for (String role : roles.split(","))
				if (user.hasRole(role))
					return true;
		}		
		return false;
	}


	/**
	 * @see info.magnolia.module.templatingkit.sites.TemplateAvailability#isAvailable(info.magnolia.cms.core.Content, info.magnolia.module.templatingkit.templates.STKTemplate)
	 */
	@Override
	public boolean isAvailable(Node node, STKPage template) {
		try {
			Node parent = node.getParent();
			if (parent!=null && parent.getDepth()>0){
				Boolean result = isAvailableFromParent(parent, template);
				if (result!=null)
					return result;
			}
		} catch (RepositoryException | RegistrationException e) {
			log.warn("Ignoring exception while computing allowed templates. Falling back to default template-availability",e);
		}
		return super.isAvailable(node, template);
	}

	
}
