//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.discussion.*;
import com.plumtree.remote.prc.collaboration.discussion.IDiscussionManager;
import com.plumtree.remote.prc.collaboration.discussion.IDiscussion;
import com.plumtree.remote.prc.collaboration.discussion.IDiscussionMessage;


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 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.
 */
public class DiscussionCommandLineExample
{

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

    //creates a discussion message
    public static IDiscussionMessage createAndStoreMessage(IDiscussion discussion, String name, String description) throws Exception
    {
        //create the discussion message
        IDiscussionMessage message = discussion.createDiscussionMessage(name, description);

        //sets optional message properties
        message.setApproved(true);
        message.setDescription("Option description for discussion message");

        //store() needs to be called to persist the message
        message.store();
        System.out.println("\nCreated message has subject=" + message.getSubject() + ", body=" + message.getBody() + ", ID=" + message.getID() + ", description=" + message.getDescription());
        return message;
    }

    //creates a reply
    public static IDiscussionMessage createAndStoreReply(IDiscussionMessage parentMessage, String subject, String body) throws Exception
    {
        //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.setDescription("Optional description for reply message.");
        reply.setApproved(true);

        //store the reply message to get the ID
        reply.store();
        System.out.println("\nCreated reply message has subject=" + reply.getSubject() + ", body=" + reply.getBody() + ", ID=" + reply.getID() + ", description=" + reply.getDescription());
        return reply;
    }


    //queries discussions in a project
    public static IDiscussion[] searchDiscussions(IProject project) throws Exception
    {
        IDiscussionFilter discussionFilter = discussionManager.createDiscussionFilter();

        //search for all discussions
        
        //limit the return results to be 10; setting this to 0 will return all results
        discussionFilter.setMaximumResults(10);

        //disable security checking on the returned objects against the user who performs this query,
        //so that all objects will be returned
        discussionFilter.setRestoreSecurity(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);

        System.out.println("\nSearching for discussion in project with name=" + project.getName() + " gets back " + retrievedDiscussions.length + " results.");
        for (int i = 0; i < retrievedDiscussions.length; i++)
        {
            System.out.println("Retrieved discussion[" + i + "] has name=" + retrievedDiscussions[i].getName() + ", ID=" + retrievedDiscussions[i].getID());
        }
        return retrievedDiscussions;
    }

    //queries messages in a discussion
    public static IDiscussionMessage[] searchMessages(IDiscussion discussion) throws Exception
    {
        IDiscussionMessageFilter messageFilter = discussionManager.createDiscussionMessageFilter();

        //search for ALL messages; other options include searching for APPROVED, or UNAPPROVED messages
        messageFilter.setMessageStatusType(DiscussionMessageStatusFilterType.ALL);

        //limit the return results to be 10; setting this to 0 will return all results
        messageFilter.setMaximumResults(10);

        //disable security checking on the returned objects against the user who performs this query,
        //so that all objects will be returned
        messageFilter.setRestoreSecurity(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(DiscussionMessageAttribute.CREATED, false);
        messageFilter.setQueryOrders(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);

        System.out.println("\nSearching for messages in discussion with name=" + discussion.getName()
                + ", and DiscussionMessageStatusFilterType set to "
                + DiscussionMessageStatusFilterType.ALL + " gets back "
                + retrievedMessages.length + " results.");
        for (int i = 0; i < retrievedMessages.length; i++)
        {
            System.out.println("Retrieved messages[" + i + "] has subject=" + retrievedMessages[i].getSubject() + ", ID=" + retrievedMessages[i].getID());
        }
        return retrievedMessages;
    }

    //removes a discussion
    public static void removeDiscussion(IDiscussion discussion) throws Exception
    {
        int discussionID = discussion.getID();
        System.out.println("\nTrying to remove discussion with name=" + discussion.getName() + ", ID=" + discussionID);
        discussionManager.removeDiscussion(discussion);

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

    //removes a message a discussion
    public static void removeMessage(IDiscussion discussion, IDiscussionMessage message)
            throws Exception
    {
        int messageID = message.getID();
        System.out.println("\nTrying to remove message with subject=" + message.getSubject() + ", ID=" + messageID);
        discussion.removeDiscussionMessage(message);

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

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

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

}