//import IRemoteSession explicitly to avoid naming conflicts
import com.plumtree.remote.prc.IRemoteSession;

import com.plumtree.remote.prc.RemoteSessionFactory;
import com.plumtree.remote.prc.collaboration.ICollaborationFactory;
import com.plumtree.remote.prc.collaboration.project.IProject;
import com.plumtree.remote.prc.collaboration.project.IProjectManager;
import com.plumtree.remote.prc.collaboration.security.*;
import com.plumtree.remote.prc.collaboration.discussion.*;


import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Command line example which demonstrates security management for different
 * collaboration objects, such as adding a member and a group to a project role,
 * setting the access level for a role, and setting the access level on individual
 * collaboration objects. 
 */
public class SecurityCommandLineExample
{
    //constants for endpoint, username, password and other command line args
    public static final String ENDPOINT = "-endpoint";
    public static final String USERNAME = "-username";
    public static final String PASSWORD = "-password";

    private static IProjectManager projectManager;
    private static IDiscussionManager discussionManager;
   
    public static void main(String[] args) throws Exception
    {
        //make the arguments into a hashmap
        Map argsMap = parseParameters(args);

        //get a remote session using the soap endpoint, username, and password              
        URL endpoint = (URL) argsMap.get(ENDPOINT);
        String username = (String) argsMap.get(USERNAME);
        String password = (String) argsMap.get(PASSWORD);
        

        IRemoteSession session = RemoteSessionFactory.getExplicitLoginContext(endpoint, username, password);

        //get a collab factory, a project manager, and a discussion manager
        ICollaborationFactory collabFactory = session.getCollaborationFactory();
        projectManager = collabFactory.getProjectManager();
        discussionManager = collabFactory.getDiscussionManager();

        //create and store a project
        IProject project = createAndStoreProject("Sample Project", "Description for Sample Project");
        
        //retrieve the member role of the project 
        IRole memberRole = project.getRole(RoleType.MEMBER);
        
        //add administrator (id=1) to the member role
        addMemberToRole(memberRole, 1);
        
        //change the member role to have ADMIN access level for DOCUMENT functional area 
        setAccessLevelForFunctionalArea(memberRole, AccessLevel.ADMIN, FunctionalArea.DOCUMENT);
        
        //now create and store a discussion
        IDiscussion discussion = createAndStoreDiscussion(project, "Sample Discussion", "Description for Sample Discussion");

        setAccessLevelForDiscussion(discussion, RoleType.MEMBER, AccessLevel.READ);

        //add administrator (id=1) to the member role
        removeMemberFromRole(memberRole, 1);


    }

    //creates a project - please see Project Example for detailed documentation
    public static IProject createAndStoreProject(String name, String description)
            throws Exception
    {
        IProject project = projectManager.createProject(name, description);
        project.store();
        return project;
    }

    //creates a discussion
    public static IDiscussion createAndStoreDiscussion(IProject project, String name, String description) throws Exception
    {
        IDiscussion discussion = discussionManager.createDiscussion(project, name, description);

        //store the discussion to get the ID
        discussion.store();
        System.out.println("Created discussion has name=" + discussion.getName() + ", ID=" + discussion.getID());
        return discussion;
    }

    //add a member to a role
    public static void addMemberToRole(IRole role, int userID) throws Exception
    {
        //add the member to the role 
        role.addMember(userID, MemberType.USER);
        
        //store the role to persist change
        role.store();
        
        int[] memberIDs = role.getMemberIDs(MemberQueryType.ALL_USERS);
        System.out.println("Updated role " + role.getName() + " - added user to role with ID = " + memberIDs[0]);
       
    }

    //remove a member from a role
    public static void removeMemberFromRole(IRole role, int userID) throws Exception
    {
        //remove the member from the role 
        role.removeMember(userID, MemberType.USER);
        
        //store the role to persist change
        role.store();
        
        int[] memberIDs = role.getMemberIDs(MemberQueryType.ALL_USERS);
        if (memberIDs.length == 0) 
        {
            System.out.println("Successfully removed " + userID + " from role " + role.getName());       
        }
        else
        {
            System.out.println("Failed to remove " + userID + " from role " + role.getName());                   
        }
    }
    
    public static void setAccessLevelForFunctionalArea(IRole role, AccessLevel accessLevel, FunctionalArea functionalArea) throws Exception
    {        
        //change role to have the supplied access level for the functional area
        role.setAccessLevel(functionalArea, accessLevel);
        
        //store the role to persist the access level change
        role.store();
        
        System.out.println("Updated role " + role.getName() + " with access level set to " + role.getAccessLevel(functionalArea) + " for " + functionalArea);  
    }

    public static void setAccessLevelForDiscussion(IDiscussion discussion, RoleType roleType, AccessLevel accessLevel) throws Exception
    {
        //first check if the current user has the permission to edit security for the discussion
        boolean hasPermission = discussion.isActionAllowed(DiscussionPermission.EDIT_SECURITY);

        if (hasPermission)
        {
            System.out.println("Current user has permission to edit security on discussion.");
        
	        //first disable using project default security
	        discussion.setDefaultSecurity(false);
	        //then change discussion to have the supplied access level for the role type
	        discussion.setAccessLevel(roleType, accessLevel);
	        
	        //store the discussion to persist the access level change
	        discussion.store();
	        
	        System.out.println("Set discussion with name=" + discussion.getName() + " to have access level " + discussion.getAccessLevel(roleType) + " for " + roleType);  
        }
        else
        {
            System.out.println("Current user does not have sufficient permission to edit security on discussion.");
        }
    }
    

    //helper method to parse command line parameters
    public static Map parseParameters(String[] args) throws Exception
    {
        Map map = new HashMap();
        //make the args into a list for easier processing
        ArrayList argsList = new ArrayList(Arrays.asList(args));

        //put the args into the map
        getArg(map, argsList, ENDPOINT, "Endpoint not specified");
        getArg(map, argsList, USERNAME, "Username not specified");
        getArg(map, argsList, PASSWORD, "Password not specified");
        return map;
    }

    //helper method to get parameters
    public static void getArg(Map map, ArrayList argsList, String arg,
            String errorMessage) throws Exception
    {
        //get the position of the argument
        int position = argsList.indexOf(arg);

        //if any required arguments not found, squawk and quit
        if (position == -1)
        {
            errorUsage(errorMessage);
        }
        else
        {
            Object value = null;
            //check for index out of bounds exceptions
            if (position + 1 == argsList.size())
            {
                errorUsage("Unable to find corresponding argument for " + arg);
            }
            value = argsList.get(position + 1);

            if (arg.equals(ENDPOINT))
            {
                value = new URL((String) value);
            }
            map.put(arg, value);
        }
    }

    //method to print out an error message, usage, and exit
    public static void errorUsage(String errorMessage)
    {
        System.out.println(errorMessage);
        System.out.println("\n");
        printUsage();
        System.exit(1);
    }

    //prints usage
    public static void printUsage()
    {
        StringBuffer usage = new StringBuffer();
        usage
                .append("\n\nUsage: java SecurityCommandLineExample \n")
                .append(ENDPOINT)
                .append(" http://myserver:8080/ptapi/services/QueryInterfaceAPI \n")
                .append(USERNAME).append(" administrator -password \"\"\n");

        System.out.println(usage.toString());
    }

}