using System;

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

using Plumtree.Remote.PRC.Collaboration.Project;
using Plumtree.Remote.PRC.Collaboration.Discussion;
using RemoteSessionFactory = Plumtree.Remote.PRC.RemoteSessionFactory;
using Plumtree.Remote.PRC.Collaboration;

using System.Collections;
using System.Text;

/// <summary>
/// Command line example which demonstrates simple discussion functionality,
/// such as creating a discussion, searching for a discussion, removing a discussion, 
/// creating a post, creating a reply, searching for posts, and removing a post.
/// </summary>

public class DiscussionCommandLineExample
{
	//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";

    private static IProjectManager projectManager;
    private 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 functionality -----

        //create and store the containing project
        IProject project = CreateAndStoreProject("Sample Project", "Description for Sample Project");

        //create and store a discussion
        IDiscussion discussion = CreateAndStoreDiscussion(project, "Sample Discussion", "Description for Sample Discussion");

        //create and store a discussion message
        IDiscussionMessage message = CreateAndStoreMessage(discussion, "Sample Message 1", "Description for Sample Message 1");

        //create and store a reply to the message created
        IDiscussionMessage reply = CreateAndStoreReply(message, "Sample Reply", "Description for Sample Reply");

        // ----- SEARCH functionality -----

        //search for all discussions in current project, should retrieve 1 discussion
        SearchDiscussions(project);

        //search for all messages in current discussion, should retrieve 2 messages
        //results will be sorted by CREATED_DATE in descending order
        SearchMessages(discussion);


        // ----- REMOVE functionality -----

        //remove the first message from its containing discussion
        RemoveMessage(discussion, message);

        //remove the specified discussion
        RemoveDiscussion(discussion);

    }

    //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("\nCreated discussion has name=" + discussion.Name + ", ID=" + discussion.ID);
        return discussion;
    }

    //creates a discussion message
    public static IDiscussionMessage CreateAndStoreMessage(IDiscussion discussion, String name, String description)
    {
        //create the discussion message
        IDiscussionMessage message = discussion.CreateDiscussionMessage(name, description);

        //sets optional message properties 
        message.Approved = true;
        message.Description = "Option description for discussion message";

        //store() needs to be called to persist the message
        message.Store();
        Console.WriteLine("\nCreated message has subject=" + message.Subject + ", body=" + message.Body + ", ID=" + message.ID + ", description=" + message.Description);
        return message;
    }

    //creates a reply
    public static IDiscussionMessage CreateAndStoreReply(IDiscussionMessage parentMessage, String subject, String body)
    {
        //create the reply; CreateDiscussionReplyMessage() will create a persisted message, so
        //Store() does not need to be called to persist the reply message properties unless additional properties are set.
        IDiscussionMessage reply = parentMessage.CreateDiscussionReplyMessage(subject, body);

        // if you want to set additional properties, make sure that Store() is called or the changes will not be persisted.
        reply.Description = "Optional description for reply message.";
        reply.Approved = true;

        //store the reply message to get the ID
        reply.Store();
        Console.WriteLine("\nCreated reply message has subject=" + reply.Subject + ", body=" + reply.Body + ", ID=" + reply.ID + ", description=" + reply.Description);
        return reply;
    }


    //queries discussions in a project
    public static IDiscussion[] SearchDiscussions(IProject project)
    {
        IDiscussionFilter discussionFilter = discussionManager.CreateDiscussionFilter();

        //search for all discussions
        
        //limit the return results to be 10; setting this to 0 will return all results
        discussionFilter.MaximumResults = 10;

        //disable security checking on the returned objects against the user who performs this query,
        //so that all objects will be returned
        discussionFilter.RestoreSecurity = false;

        //an array of IDiscussion are returned from queryDiscussions(); if no result is retrieved, a zero-length array will be returned
        IDiscussion[] retrievedDiscussions = discussionManager.QueryDiscussions(project, discussionFilter);

        Console.WriteLine("\nSearching for discussion in project with name=" + project.Name + " gets back " + retrievedDiscussions.Length + " results.");
        for (int i = 0; i < retrievedDiscussions.Length; i++)
        {
            Console.WriteLine("Retrieved discussion[" + i + "] has name=" + retrievedDiscussions[i].Name + ", ID=" + retrievedDiscussions[i].ID);
        }
        return retrievedDiscussions;
    }

    //queries messages in a discussion
    public static IDiscussionMessage[] SearchMessages(IDiscussion discussion)
    {
        IDiscussionMessageFilter messageFilter = discussionManager.CreateDiscussionMessageFilter();

        //search for ALL messages; other options include searching for APPROVED, or UNAPPROVED messages
        messageFilter.MessageStatusType = DiscussionMessageStatusFilterTypes.All;

        //limit the return results to be 10; setting this to 0 will return all results
        messageFilter.MaximumResults = 10;

        //disable security checking on the returned objects against the user who performs this query,
        //so that all objects will be returned
        messageFilter.RestoreSecurity = false;

        //user DiscussionMessageQueryOrder to sort the query result; below DiscussionMessageQueryOrder
        //shows sorting the returned messages by CREATED date in descending order
        DiscussionMessageQueryOrder messageQueryOrder = new DiscussionMessageQueryOrder(DiscussionMessageAttributes.Created, false);
        messageFilter.QueryOrders = new DiscussionMessageQueryOrder[] { messageQueryOrder };

        //an array of IDiscussionMessage objects are returned from queryDiscussionMessages(); if no result is retrieved, a zero-length array will be returned
        IDiscussionMessage[] retrievedMessages = discussionManager.QueryDiscussionMessages(discussion, messageFilter);

        Console.WriteLine("\nSearching for messages in discussion with name=" + discussion.Name
                + ", and DiscussionMessageStatusFilterType set to "
                + DiscussionMessageStatusFilterTypes.All + " gets back "
                + retrievedMessages.Length + " results.");
        for (int i = 0; i < retrievedMessages.Length; i++)
        {
            Console.WriteLine("Retrieved messages[" + i + "] has subject=" + retrievedMessages[i].Subject + ", ID=" + retrievedMessages[i].ID);
        }
        return retrievedMessages;
    }

    //removes a discussion
    public static void RemoveDiscussion(IDiscussion discussion)
    {
        int discussionID = discussion.ID;
        Console.WriteLine("\nTrying to remove discussion with name=" + discussion.Name + ", ID=" + discussionID);
        discussionManager.RemoveDiscussion(discussion);

        //trying to retrieve the discussion after removal, should retrieve nothing
        IDiscussion discussionAfterRemoval = discussionManager.GetDiscussion(discussionID);
        if (discussionAfterRemoval == null)
        {
            Console.WriteLine("Discussion successfully removed.");
        }
        else
        {
            Console.WriteLine("Failed to remove discussion.");
        }
    }

    //removes a message a discussion
    public static void RemoveMessage(IDiscussion discussion, IDiscussionMessage message)
           
    {
        int messageID = message.ID;
        Console.WriteLine("\nTrying to remove message with subject=" + message.Subject + ", ID=" + messageID);
        discussion.RemoveDiscussionMessage(message);

        //trying to retrieve the message after removal, should retrieve nothing
        IDiscussionMessage messageAfterRemoval = discussionManager.GetDiscussionMessage(messageID);
        if (messageAfterRemoval == null)
        {
            Console.WriteLine("Message successfully removed.");
        }
        else
        {
            Console.WriteLine("Failed to remove message.");
        }
    }

	//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: DiscussionCommandLineExample \n")
             .Append(ENDPOINT)
             .Append(" http://myserver:8080/ptapi/services/QueryInterfaceAPI \n")
             .Append(USERNAME)
			 .Append(" administrator -password \"\"\n");

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

}