using System;
//import IRemoteSession explicitly to avoid naming conflicts
using IRemoteSession = Plumtree.Remote.PRC.IRemoteSession;

using RemoteSessionFactory = Plumtree.Remote.PRC.RemoteSessionFactory;


using Plumtree.Remote.PRC.Collaboration;
using Plumtree.Remote.PRC.Collaboration.Project;
using Plumtree.Remote.PRC.Collaboration.Discussion;
using Plumtree.Remote.PRC.Collaboration.Security;
using System.Collections;
using System.Text;


/**
 * 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 readonly String ENDPOINT = "-endpoint";
	public static readonly String USERNAME = "-username";
	public static readonly String PASSWORD = "-password";

	static IProjectManager projectManager;
	static IDiscussionManager discussionManager;

   
    public static void Main(String[] args)
    {
		//make the arguments into a hashtable
		Hashtable argsMap = ParseParameters(args);

		//get a remote session using the soap endpoint, username, and password
		System.Uri endpoint = (Uri) argsMap[ENDPOINT];
		String username = (String) argsMap[USERNAME];
		String password = (String) argsMap[PASSWORD];
		IRemoteSession session = RemoteSessionFactory.GetExplicitLoginContext(endpoint, username, password);

		//get a collab factory and a project 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(RoleTypes.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, AccessLevels.Admin, FunctionalAreas.Document);
        
        //now create and store a discussion
        IDiscussion discussion = CreateAndStoreDiscussion(project, "Sample Discussion", "Description for Sample Discussion");

        SetAccessLevelForDiscussion(discussion, RoleTypes.Member, AccessLevels.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)
           
    {
        IProject project = projectManager.CreateProject(name, description);
        project.Store();
        return project;
    }

    //creates a discussion
    public static IDiscussion CreateAndStoreDiscussion(IProject project, String name, String description)
    {
        IDiscussion discussion = discussionManager.CreateDiscussion(project, name, description);

        //store the discussion to get the ID
        discussion.Store();
        Console.WriteLine("Created discussion has name=" + discussion.Name + ", ID=" + discussion.ID);
        return discussion;
    }

    //add a member to a role
    public static void AddMemberToRole(IRole role, int userID)
    {
        //add the member to the role 
        role.AddMember(userID, MemberTypes.User);
        
        //store the role to persist change
        role.Store();
        
        int[] memberIDs = role.GetMemberIDs(MemberQueryTypes.AllUsers);
        Console.WriteLine("Updated role " + role.Name + " - added user to role with ID = " + memberIDs[0]);
       
    }

    //remove a member from a role
    public static void RemoveMemberFromRole(IRole role, int userID)
    {
        //remove the member from the role 
        role.RemoveMember(userID, MemberTypes.User);
        
        //store the role to persist change
        role.Store();
        
        int[] memberIDs = role.GetMemberIDs(MemberQueryTypes.AllUsers);
        if (memberIDs.Length == 0) 
        {
            Console.WriteLine("Successfully removed " + userID + " from role " + role.Name);       
        }
        else
        {
            Console.WriteLine("Failed to remove " + userID + " from role " + role.Name);                   
        }
    }
    
    public static void SetAccessLevelForFunctionalArea(IRole role, AccessLevels accessLevel, FunctionalAreas functionalArea)
    {        
        //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();
        
        Console.WriteLine("Updated role " + role.Name + " with access level set to " + role.GetAccessLevel(functionalArea) + " for " + functionalArea);  
    }

    public static void SetAccessLevelForDiscussion(IDiscussion discussion, RoleTypes roleType, AccessLevels accessLevel)
    {
        //first check if the current user has the permission to edit security for the discussion
        bool hasPermission = discussion.IsActionAllowed(DiscussionPermissions.EditSecurity);

        if (hasPermission)
        {
            Console.WriteLine("Current user has permission to edit security on discussion.");
        
	        //first disable using project default security
	        discussion.DefaultSecurity = 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();
	        
	        Console.WriteLine("Set discussion with name=" + discussion.Name + " to have access level " + discussion.GetAccessLevel(roleType) + " for " + roleType);  
        }
        else
        {
            Console.WriteLine("Current user does not have sufficient permission to edit security on discussion.");
        }
    }
    

	//helper method to parse command line parameters
	public static Hashtable ParseParameters(String[] args) 
	{
		Hashtable map = new Hashtable();
		//make the args into a list for easier processing
		ArrayList argsList = new ArrayList();
		for (int i = 0; i < args.Length; i++)
		{
			argsList.Add(args[i]);
		}
		//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(Hashtable map, ArrayList argsList, String arg, String errorMessage) 
	{
		//get the position of the argument        
		int position = argsList.IndexOf(arg);

		//if not found and required, squawk and quit
		if (position == -1)
		{
			ErrorUsage(errorMessage);
		}
		else
		{
			//special case for create, remove, and search
			Object value = null;
			//check for index out of bounds exceptions
			if (position + 1 == argsList.Count)
			{
				ErrorUsage("Unable to find corresponding argument for " + arg);
			}
			value = argsList[position + 1];

			//special case for endpoint and project id
			if (arg.Equals(ENDPOINT))
			{
				value = new Uri((String) value);
			}
			//only add things once
			if (!map.ContainsKey(arg))
			{
				map.Add(arg, value);
			}
		}
	}


	//method to print out an error message, usage, and exit
	public static void ErrorUsage(String errorMessage)
	{
		Console.WriteLine(errorMessage);
		Console.WriteLine("\n");
		PrintUsage();
		Environment.Exit(2);
	}

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

		Console.WriteLine(usage.ToString());
	}

}