/*
 * Created on 21-Feb-2005
 */
package org.bodington.servlet.facilities;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.bodington.server.BuildingServer;
import org.bodington.server.BuildingServerException;
import org.bodington.server.realm.Acl;
import org.bodington.server.realm.AclEntry;
import org.bodington.server.realm.Group;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.server.resources.Resource;

/**
 * Class for working out the acls for a resource.
 * @author buckett
 */
public class AclResource
{
    
    private Resource resource;
    private boolean includeAdmin;
    private Map users = new HashMap();
    private Map groups = new HashMap();

    public AclResource(Resource resource) throws BuildingServerException
    {
        this(resource, false);
    }
    
    public AclResource(Resource resource, boolean includeAdmin) throws BuildingServerException
    {
        this.resource = resource;
        this.includeAdmin = includeAdmin;
        addCurrentUser();
        calculate(resource);
        // Need to update all the visible users with the collapsed groups.
        updateOverlapping();
    }
    
    /**
     * Gets a set containing @{link AclResourcePermission} objects keyed on @{link User} objects.
     */
    public Set getResults()
    {
        TreeSet results =new TreeSet();
        results.addAll(users.values());
        results.addAll(groups.values());
        return results;
    }
   
    /**
     * Get a @{link java.util.Set} containing just the @{link AclResourcePermission} for the
     * user. 
     * @param user The user to get the permission for.
     * @return A Set containing one entry.
     */
    public Set getResults(User user)
    {
        TreeSet results = new TreeSet();
        results.add(users.get(user));
        return results;
    }

    /**
     * Added the current user to the list of user permissions.
     * @throws BuildingServerException
     */
    private void addCurrentUser() throws BuildingServerException
    {
        // Make sure the current use is always in the list
        AclUserResourcePermission permission = new AclUserResourcePermission();
        User user = (User)BuildingServer.getInstance().getContext().getUser();
        if (user != null)
        {
            permission.setUser(user);
            users.put(user, permission);
        }
    }
    
    /**
     * Calculates the permissions summary.
     * @param resource The resource to check.
     * @throws BuildingServerException
     */
    private void calculate (Resource resource) throws BuildingServerException 
    {
        
        Acl acl = resource.getAcl();
        AclEntry aclEntry;
        Enumeration aclEntries = acl.entries();
        while(aclEntries.hasMoreElements())
        {
            aclEntry = (AclEntry)aclEntries.nextElement();
            Group group = aclEntry.getGroup();
            Enumeration members = (group.isSpecialGroup() || group.size() == 0 || group.size() > 10)? null :  group.membersChecked();
            if (members == null)
            {
                AclGroupResourcePermission permission = getGroupPermission(group);
                permission.setGroup(group);
                permission.addPermissions(aclEntry);
            }
            else 
            {
                while(members.hasMoreElements())
                {
                    User member = (User)members.nextElement();
                    AclUserResourcePermission permission = getUserPermission(member);
                    permission.addPermissions(aclEntry);
                    permission.addGroup(group);
                }
            }
            
        }
        if (resource.getParent() != null)
        {
            if (resource.getUseParentAcl() )
                calculate(resource.getParent());
            else if (includeAdmin)
                calculateAdmin(resource.getParent());
        }

    }
    
    private void calculateAdmin (Resource resource) throws BuildingServerException 
    {
        
        Acl acl = resource.getAcl();
        AclEntry aclEntry;
        Enumeration aclEntries = acl.entries();
        while(aclEntries.hasMoreElements())
        {
            aclEntry = (AclEntry)aclEntries.nextElement();
            Group group = aclEntry.getGroup();
            if (!(aclEntry.checkPermission(Permission.ADMINISTER) || aclEntry
                .checkPermission(Permission.SYSADMIN)))
                continue;
            Enumeration members = (group.isSpecialGroup() || group.size() == 0 || group.size() > 10)? null :  group.membersChecked();
            if (members == null)
            {
                AclGroupResourcePermission permission = getGroupPermission(group);
                permission.setGroup(group);
                permission.addAdminPermissions(aclEntry);

            }
            else 
            {
                while(members.hasMoreElements())
                {
                    User member = (User)members.nextElement();
                    AclUserResourcePermission permission = getUserPermission(member);
                    permission.addGroup(group);
                    permission.addAdminPermissions(aclEntry);
                }
            }
            
        }
        if (resource.getParent() != null)
        {
            calculateAdmin(resource.getParent());
        }

    }
    
    /**
     * Update users who are in colapsed groups.
     * If we didn't display the contents of a group because it was too big or the 
     * user didn't have permission we need to update the users which can be seen 
     * with the permissions of that group and add to the users list.
     */
    private void updateOverlapping()
    {
        Iterator usersIterator = users.keySet().iterator();
        while(usersIterator.hasNext())
        {
            User user = (User)usersIterator.next();
            AclUserResourcePermission userAcl = (AclUserResourcePermission)users.get(user);
            Iterator groupsIterator = groups.keySet().iterator();
            while(groupsIterator.hasNext())
            { 
                Group group = (Group)groupsIterator.next();
                AclGroupResourcePermission groupAcl = (AclGroupResourcePermission)groups.get(group);
                if (user.isMember(group)) {
                    userAcl.addGroup(group);
                    userAcl.addPermissions(groupAcl);
                }
            }
        }
        
    }
    
    private AclGroupResourcePermission getGroupPermission(Group group)
    {
        AclGroupResourcePermission permission = (AclGroupResourcePermission) groups
            .get(group);
        if (permission == null)
        {
            permission = new AclGroupResourcePermission();
            groups.put(group, permission);
        }
        return permission;
    }

private AclUserResourcePermission getUserPermission(User user)
    {
        AclUserResourcePermission permission = (AclUserResourcePermission)users.get(user);
        if (permission == null)
        {
            permission = new AclUserResourcePermission();
            permission.setUser(user);
            users.put(user, permission);
        }
        return permission;
    }
    

}
