//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.tasklist.*;
import com.plumtree.remote.prc.collaboration.tasklist.ITaskListManager;
import com.plumtree.remote.prc.collaboration.tasklist.ITaskList;
import com.plumtree.remote.prc.collaboration.tasklist.ITask;


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

/**
* Command line example which demonstrates simple task list functionality, such as
* creating a task list, searching for a task list, removing a task list.
*/
public class TaskListCommandLineExample
{

  //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 ITaskListManager tasklistManager;

  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 task list manager
      ICollaborationFactory collabFactory = session.getCollaborationFactory();
      projectManager = collabFactory.getProjectManager();
      tasklistManager = collabFactory.getTaskListManager();

      
      // ----- CREATE functionality ----- 
      
      //create and store the containing project
      IProject project = createAndStoreProject("Sample Project", "Description for Sample Project");
      
      //create and store a task list
      ITaskList tasklist = createAndStoreTaskList(project, "Sample TaskList", "Description for Sample TaskList");

      //create and store a top-level task, with startTime set to 9/1/2004, endTime set to 9/8/2004
      ITask toplevelTask = createAndStoreTopLevelTask(tasklist, "Sample TopLevel Task 1", "Description for Sample TopLevel Task 1", new Date(2004, 9, 1), new Date(2004, 9, 8));
      
      //create and store a sub-task, with startTime set to 9/2/2004, endTime set to 9/4/2004
      ITask subTask = createAndStoreSubTask(toplevelTask, "Sample Sub Task", "Description for Sample Sub Task", new Date(2004, 9, 2), new Date(2004, 9, 4));
      
      //create and store another toplevel task, which is going to be added as a dependent task of subTask 
      ITask dependentTask = createAndStoreDependentTask(tasklist, "Dependent Task", "Task that depends on Toplevel Task 1", new Date(2004, 9, 8), new Date(2004, 9, 10), subTask);
      
      
      // ----- SEARCH functionality -----
      
      //search for all task lists in current project, should retrieve 1 task list
      searchTaskLists(project);
      
      //search for all tasks in current task list, should retrieve 3 tasks
      //results will be sorted by NAME in ascending order
      searchTasks(tasklist);
      
      // ----- COPY functionality -----
      
      //create a target project for copying task lists from source project 
      IProject targetProject = createAndStoreProject("Target Project for Copy", "Description for target Project");
      copyTaskLists(project, targetProject, new ITaskList[] { tasklist } );
            
      // ----- REMOVE functionality -----
      
      //remove the toplevelTask from its containing task list
      removeTask(tasklist, toplevelTask);

      //remove the specified task list
      removeTasklist(tasklist);
      
  }

  //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 task list
  public static ITaskList createAndStoreTaskList(IProject project, String name, String description) throws Exception
  {
      ITaskList tasklist = tasklistManager.createTaskList(project, name, description);
     
      //store the task list to get the ID
      tasklist.store();
      System.out.println("\nCreated task list has name=" + tasklist.getName() + ", ID=" +  tasklist.getID());
      return tasklist;
  }

  
  //creates a task
  public static ITask createAndStoreTopLevelTask(ITaskList tasklist, String name, String description, Date startTime, Date endTime) throws Exception
  {
      //create the task
      ITask task = tasklist.createTask(name, description, startTime, endTime);
      
      // if you want to set additional properties, make sure that store() is 
      // called or the changes will not be persisted.
      task.setNotes("Notes for sample TopLevel Task");
      task.setRisk(TaskRisk.MEDIUM);
      task.setStatus(TaskStatus.PENDING);
     
      //store the task to get the ID
      task.store();
      System.out.println("\nCreated top-level task has name=" + task.getName() + ", ID=" + task.getID() + ", risk=" + task.getRisk() + ", status=" + task.getStatus() + ", and notes=" + task.getNotes());
      return task;
  }
  
  //creates a subtask
  public static ITask createAndStoreSubTask(ITask parentTask, String name, String description, Date startTime, Date endTime) throws Exception
  {
      //create the subtask; createSubTask() will create a persisted task, so
      // store() does not need to be called to persist the task properties unless additional properties are set.
      ITask subtask = parentTask.createSubTask(name, description, startTime, endTime);
      
      // if you want to set additional properties, make sure that store() is 
      // called or the changes will not be persisted.
      subtask.setRisk(TaskRisk.LOW);
      subtask.setStatus(TaskStatus.FIFTY_PERCENT_COMPLETED);
     
      //store the subtask to get the ID
      subtask.store();
      System.out.println("\nCreated sub-task has name=" + subtask.getName() + ", ID=" + subtask.getID() + ", and parentTask=" + subtask.getParentTask().getName());
      return subtask;
  }
  
  //creates a task, and then add the source task as its dependency
  public static ITask createAndStoreDependentTask(ITaskList tasklist, String name, String description, Date startTime, Date endTime, ITask sourceTask) throws Exception
  {
      //create a toplevel task first
      ITask dependentTask = tasklist.createTask(name, description, startTime, endTime);
      
      //add the sourceTask as the current task's dependency, make sure that store() is 
      //called after addDependentTask(), similar to the other task properties,
      //otherwise the newly-added dependency will not be persisted.
      dependentTask.addDependentTask(sourceTask);
     
      //store the task to persist the added dependency and get the ID
      dependentTask.store();
      
      //Notice that upon adding a dependent task, the start time of the task will be set automatically to 
      //the end time of dependent source task.
      
      //retrieve all source tasks that this tasks depends on
      ITask[] taskDependency = dependentTask.getTaskDependencies();      
      String taskDependencyName = taskDependency[0].getName();
      
      System.out.println("\nCreated task has name=" + dependentTask.getName() + ", ID=" + dependentTask.getID() + ", which has a dependency on sourceTask=" + taskDependencyName);
      return dependentTask;
  }
  
  
  //queries task lists in a project
  public static ITaskList[] searchTaskLists(IProject project) throws Exception
  {
      ITaskListFilter taskListFilter = tasklistManager.createTaskListFilter();
            
      //set the query to search for all task list
      // different options can be set to search on task lists that contain only PENDING tasks, 
      // completed tasks, or overdue tasks.  See documentation in TaskListCompletionFilterType
      taskListFilter.setCompletionType(TaskListCompletionFilterType.ALL);
      
      //limit the return results to be 10; setting this to 0 will return all results
      taskListFilter.setMaximumResults(10);
      
      //disable security checking on the returned objects against the user who performs this query, 
      //so that all objects will be returned
      taskListFilter.setRestoreSecurity(false);
      
      //user TaskListQueryOrder to sort the query result; below TaskListQueryOrder shows sorting the returned task lists by NAME in ascending order
      TaskListQueryOrder taskListQueryOrder = new TaskListQueryOrder(TaskListAttribute.NAME, true);
      taskListFilter.setQueryOrders(new TaskListQueryOrder[] {taskListQueryOrder} );
      
      //an array of ITaskList objects are returned from queryTaskLists(); if no result is retrieved, a zero-length array will be returned
      ITaskList[] retrievedTaskLists = tasklistManager.queryTaskLists(project, taskListFilter);
      
      System.out.println("\nSearching for task list in project with name=" + project.getName() + ", and TaskListCompletionFilterType set to "+ TaskListCompletionFilterType.ALL + " gets back " + retrievedTaskLists.length + " results.");
      for (int i = 0; i < retrievedTaskLists.length; i++)
      {
          System.out.println("Retrieved task list[" + i + "] has name=" + retrievedTaskLists[i].getName() + ", ID=" + retrievedTaskLists[i].getID());
      }
      return retrievedTaskLists;
  }
  
  //queries tasks in a task list
  public static ITask[] searchTasks(ITaskList tasklist) throws Exception
  {
      ITaskFilter taskFilter = tasklistManager.createTaskFilter();
      
      //search on all tasks; other options include searching for COMPLETED tasks, OVERDUE tasks, or PENDING tasks
      taskFilter.setCompletionType(TaskCompletionFilterType.ALL);
      
      //limit the return results to be 10; setting this to 0 will return all results
      taskFilter.setMaximumResults(10);
      
      //disable security checking on the returned objects against the user who performs this query, 
      //so that all objects will be returned
      taskFilter.setRestoreSecurity(false);
      
      //user TaskQueryOrder to sort the query result; below TaskQueryOrder shows sorting the returned task lists by NAME in ascending order
      TaskQueryOrder taskQueryOrder = new TaskQueryOrder(TaskAttribute.NAME, true);
      taskFilter.setQueryOrders(new TaskQueryOrder[] {taskQueryOrder} );
      
      //an array of ITask objects are returned from queryTasks(ITaskList); if no result is retrieved, a zero-length array will be returned
      ITask[] retrievedTasks = tasklistManager.queryTasks(tasklist, taskFilter);
      
      System.out.println("\nSearching for tasks in task list with name=" + tasklist.getName() + ", and TaskListCompletionFilterType set to "+ TaskListCompletionFilterType.ALL + " gets back " + retrievedTasks.length + " results.");
      for (int i = 0; i < retrievedTasks.length; i++)
      {
          System.out.println("Retrieved task[" + i + "] has name=" + retrievedTasks[i].getName() + ", ID=" + retrievedTasks[i].getID());
      }
      return retrievedTasks;
  }
  
  //removes a task list
  public static void removeTasklist(ITaskList tasklist) throws Exception
  {
      int tasklistID = tasklist.getID();
      System.out.println("\nTrying to remove task list with name=" + tasklist.getName() + ", ID=" + tasklistID);
      tasklistManager.removeTaskList(tasklist);
      
      // trying to retrieve the task list after removal, should retrieve nothing
      ITaskList taskListAfterRemoval = tasklistManager.getTaskList(tasklistID);
      if (taskListAfterRemoval == null)
      {
          System.out.println("Tasklist successfully removed.");
      }
      else
      {
          System.out.println("Failed to remove task list.");
      }
  }

  //removes a task a task list
  public static void removeTask(ITaskList tasklist, ITask task) throws Exception
  {
      int taskID = task.getID();
      System.out.println("\nTrying to remove task with name=" + task.getName() + ", ID=" + taskID);
      tasklist.removeTask(task);
      
      // trying to retrieve the task after removal, should retrieve nothing
      ITask taskAfterRemoval = tasklistManager.getTask(taskID);
      if (taskAfterRemoval == null)
      {
          System.out.println("Task successfully removed.");
      }
      else
      {
          System.out.println("Failed to remove task.");
      }
  }
  
  //copy content of a task list into another task list
  public static void copyTaskLists(IProject sourceProject, IProject targetProject, ITaskList[] tasklistsToCopy) throws Exception
  {
      System.out.println("\nTrying to copy " + tasklistsToCopy.length + " task list(s) from source project.");
      
      //iterate through the task lists to copy from source project
      for (int i = 0; i < tasklistsToCopy.length; i++)
      {
          System.out.println("Tasklist to copy [" + i + "] has name=" + tasklistsToCopy[i].getName() + ", ID=" + tasklistsToCopy[i].getID());          
      }
      tasklistManager.copyTaskLists(sourceProject, targetProject, tasklistsToCopy);
      
      //check if the task lists are copied into the target project 
      ITaskListFilter tasklistFilter = tasklistManager.createTaskListFilter();
      
      ITaskList[] copiedTasklists = tasklistManager.queryTaskLists(targetProject, tasklistFilter);
      
      System.out.println("Copied " + copiedTasklists.length + " task list(s) into target project.");

      //iterate through the task lists to copy from source project
      for (int i = 0; i < copiedTasklists.length; i++)
      {
          System.out.println("Successfully copied task list [" + i + "] has name=" + copiedTasklists[i].getName() + ", ID=" + copiedTasklists[i].getID());          
      }   
  }

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

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


}
