/**
   * Publishes a message to qpid that describes what action a user has just performed on a task.
   * These actions may be either TaskTaken or TaskCompleted. This calls checkInitialized to see if
   * there is a valid connection to qpid.
   *
   * @param task the task that has been acted upon
   * @param exchangeName the exchange name for messaging
   * @param eventName the event that correlates with the action
   * @param taskId the id of the task that was acted upon
   */
  public void amqpTaskPublish(Task task, String exchangeName, String eventName, String taskId) {
    String info = "";
    if (checkInitialized()) {
      if (eventName.equals("TaskTaken")) {

        try {
          info =
              "eventType="
                  + eventName
                  + ";"
                  + "processID="
                  + task.getProcessInstanceId()
                  + ";"
                  + "taskID="
                  + task.getId()
                  + ";"
                  + "assignee="
                  + task.getAssignee()
                  + ";";
        } catch (Exception e) {
          log.debug(e.getMessage());
        }
      } else if (eventName.equals("TaskCompleted")) {
        try {
          org.jbpm.task.Task ht = taskRepo.findById(Long.decode(taskId));
          String processName = ht.getTaskData().getProcessId();
          String processId = Long.toString(ht.getTaskData().getProcessInstanceId());
          String assignee = ht.getTaskData().getActualOwner().getId();
          info =
              "eventType="
                  + eventName
                  + ";"
                  + "processID="
                  + processName
                  + "."
                  + processId
                  + ";"
                  + "taskID="
                  + taskId
                  + ";"
                  + "assignee="
                  + assignee
                  + ";";
        } catch (Exception e) {
          log.debug(e.getMessage());
        }
      }
      sendMessage(info, exchangeName);
    }
  }
  @Override
  public Task convert(org.jbpm.task.query.TaskSummary source) {

    Task target = new Task();

    target.setDescription(source.getDescription());

    if (source.getActualOwner() != null) {
      target.setAssignee(source.getActualOwner().getId());
    }

    if (source.getCreatedOn() != null) {
      target.setCreateTime(convert(source.getCreatedOn(), XMLGregorianCalendar.class));
    }

    if (source.getExpirationTime() != null) {
      target.setDueDate(convert(source.getExpirationTime(), XMLGregorianCalendar.class));
    }
    target.setId(String.valueOf(source.getId()));

    if (source.getName() != null) {
      String[] parts = source.getName().split("/");
      target.setActivityName(parts[0]);
      target.setName(parts[1]);
    }

    target.setState(source.getStatus().name());

    target.setPriority(new Integer(source.getPriority()));
    target.setProcessInstanceId(
        source.getProcessId() + "." + Long.toString(source.getProcessInstanceId()));

    BlockingGetTaskResponseHandler getTaskResponseHandler = new BlockingGetTaskResponseHandler();
    taskClient.getTask(source.getId(), getTaskResponseHandler);
    org.jbpm.task.Task task = getTaskResponseHandler.getTask();

    BlockingGetContentResponseHandler getContentResponseHandler =
        new BlockingGetContentResponseHandler();
    taskClient.getContent(task.getTaskData().getDocumentContentId(), getContentResponseHandler);
    Content content = getContentResponseHandler.getContent();

    Map<String, Object> map =
        (Map<String, Object>) ContentMarshallerHelper.unmarshall(content.getContent(), null);

    for (String key : map.keySet()) {
      log.debug("Key: " + key);
    }

    // add task outcomes using the "Options" variable from the task
    String optionsString = (String) map.get("Options");
    if (optionsString != null) {
      String[] options = ((String) map.get("Options")).split(",");
      target.getOutcomes().addAll(Arrays.asList(options));
    }

    // get ad-hoc variables map

    Map<String, Object> contentMap =
        (Map<String, Object>) map.get(Bpmn20UserTaskNodeBuilder.TASK_INPUT_VARIABLES_NAME);
    if (contentMap != null) {
      for (Entry<String, Object> entry : contentMap.entrySet()) {
        log.debug(entry.getKey() + "=" + entry.getValue());
        addVariable(target, entry.getKey(), entry.getValue());
      }
    } else {
      log.debug("No Content found for task");
    }

    // add variables
    /*Set<String> names = taskService.getVariableNames(source.getId());
    Map<String, Object> variables = taskService.getVariables(source.getId(), names);
    // remove process name var
    variables.remove("_name");
    for (String key : variables.keySet()) {
        Variable var = new Variable();
        var.setName(key);
        // Support strings only.  Other types will cause ClassCastException
        try {
            var.setValue((String) variables.get(key));
        } catch (ClassCastException e) {
            var.setValue("Variable type " + variables.get(key).getClass().getName() + " is not supported");
        }
        addVariable(target, var);
    }

    // Do this only if the task is not an ad-hoc task (as indicated by null executionId)
    if (source.getExecutionId() != null) {

        // name is the 'form' attribute in JPDL
        // this is used in the COW schema to store the display name, as distinct from the system-generated name
        target.setName(source.getFormResourceName());

        // activityName is the 'name' from JPDL
        target.setActivityName(source.getActivityName());

        Execution ex = executionService.findExecutionById(source.getExecutionId());
        target.setProcessInstanceId(ex.getProcessInstance().getId());

        // outcomes
        Set<String> outcomes = taskService.getOutcomes(source.getId());
        for (String outcome : outcomes) {
            target.getOutcomes().add(outcome);
        }

        // Workaround to the fact that we cannot use autowiring here
        if (this.cowTaskService == null) {
            this.cowTaskService = (org.wiredwidgets.cow.server.service.TaskService) this.factory.getBean("taskService");
        }

        // add process level task varibles (
        String executionId = getTopLevelExecutionId(source.getExecutionId());
        org.wiredwidgets.cow.server.api.model.v2.Activity activity = cowTaskService.getWorkflowActivity(executionId, source.getActivityName());
        if (activity != null && activity instanceof org.wiredwidgets.cow.server.api.model.v2.Task) {
            org.wiredwidgets.cow.server.api.model.v2.Task cowTask = (org.wiredwidgets.cow.server.api.model.v2.Task) activity;
            if (cowTask.getVariables() != null) {
                for (org.wiredwidgets.cow.server.api.model.v2.Variable var : cowTask.getVariables().getVariables()) {
                    Variable newVar = new Variable();
                    newVar.setName(var.getName());
                    newVar.setValue(var.getValue());
                    addVariable(target, newVar);
                }
            }
        }
    } else {
        // for ad-hoc tasks
        target.setName(source.getName());
    }*/

    return target;
  }
 private void addVariable(Task task, Variable var) {
   if (task.getVariables() == null) {
     task.setVariables(new Variables());
   }
   task.getVariables().getVariables().add(var);
 }
  // TODO: Check if you can update a task. Can you update task by just adding a task with the same
  // ID?
  private org.jbpm.task.Task createOrUpdateTask(Task source) {

    org.jbpm.task.Task target;
    boolean newTask = false;
    if (source.getId() == null) {
      newTask = true;
      target = new org.jbpm.task.Task();
    } else {
      BlockingGetTaskResponseHandler getTaskResponseHandler = new BlockingGetTaskResponseHandler();
      taskClient.getTask(Long.valueOf(source.getId()), getTaskResponseHandler);
      target = getTaskResponseHandler.getTask();
    }
    if (target == null) {
      return null;
    }
    if (source.getAssignee() != null) {
      PeopleAssignments pa = new PeopleAssignments();
      List<OrganizationalEntity> orgEnt = new ArrayList<OrganizationalEntity>();
      org.jbpm.task.User oe = new org.jbpm.task.User();
      oe.setId(source.getAssignee());
      pa.setTaskInitiator(oe);
      orgEnt.add(oe);
      pa.setPotentialOwners(orgEnt);
      target.setPeopleAssignments(pa);
    }
    if (source.getDescription() != null) {
      List<I18NText> desc = new ArrayList<I18NText>();
      desc.add(new I18NText("en-UK", source.getDescription()));
      target.setDescriptions(desc);
    }
    if (source.getDueDate() != null) {
      Deadlines deadlines = new Deadlines();
      List<Deadline> dls = new ArrayList<Deadline>();
      Deadline dl = new Deadline();
      dl.setDate(this.convert(source.getDueDate()));
      dls.add(dl);
      deadlines.setEndDeadlines(dls);
      target.setDeadlines(deadlines);
    }
    if (source.getName() != null) {
      List<I18NText> names = new ArrayList<I18NText>();
      names.add(new I18NText("en-UK", source.getName()));
      target.setNames(names);
    }
    if (source.getPriority() != null) {
      target.setPriority(source.getPriority());
    }

    TaskData td = new TaskData();
    target.setTaskData(td);
    /*
     * if (source.getProgress() != null) {
     * target.setProgress(source.getProgress()); }
     */

    // convert variables
    /*
     * if (source.getVariables() != null &&
     * source.getVariables().getVariables().size() > 0) { Map<String,
     * Object> variables = new HashMap<String, Object>(); for (Variable
     * variable : source.getVariables().getVariables()) {
     * variables.put(variable.getName(), variable.getValue()); }
     * this.taskService.setVariables(target.getId(), variables); }
     */

    if (newTask) {
      BlockingAddTaskResponseHandler addTaskResponseHandler = new BlockingAddTaskResponseHandler();
      taskClient.addTask(target, null, addTaskResponseHandler);
    }

    return target;
  }
  @Override
  public void completeTask(
      Long id, String assignee, String outcome, Map<String, Object> variables) {
    // should be handled upstream in controller
    assert (assignee != null);

    log.debug(assignee + " starting task with ID: " + id);

    BlockingGetTaskResponseHandler getTaskResponseHandler = new BlockingGetTaskResponseHandler();
    taskClient.getTask(id, getTaskResponseHandler);
    org.jbpm.task.Task task = getTaskResponseHandler.getTask();

    // convert to COW task so we can verify the decision
    Task cowTask = converter.convert(task, Task.class);

    if (cowTask.getOutcomes() != null && cowTask.getOutcomes().size() > 0) {
      // This is a decision task!
      if (outcome == null) {
        throw new RuntimeException("ERROR: no decision provided for a Decision task");
      }
      if (!cowTask.getOutcomes().contains(outcome)) {
        throw new RuntimeException("ERROR: decision value " + outcome + " is not a valid choice.");
      }
    }

    BlockingGetContentResponseHandler getContentResponseHandler =
        new BlockingGetContentResponseHandler();
    taskClient.getContent(task.getTaskData().getDocumentContentId(), getContentResponseHandler);
    Content inputContent = getContentResponseHandler.getContent();

    Map<String, Object> inputMap =
        (Map<String, Object>) ContentMarshallerHelper.unmarshall(inputContent.getContent(), null);

    for (Map.Entry<String, Object> entry : inputMap.entrySet()) {
      log.debug(entry.getKey() + " = " + entry.getValue());
    }

    Map<String, Object> outputMap = new HashMap<String, Object>();

    // put Outcome into the outputMap
    // The InputMap contains a variable that tells us what key to use
    if (inputMap.get(DECISION_VAR_NAME) != null) {
      log.debug("Decision outcome: " + outcome);
      outputMap.put((String) inputMap.get(DECISION_VAR_NAME), outcome);
    }

    Map<String, Object> outputVarsMap = new HashMap<String, Object>();

    // NOTE: obtaining the map from the Task results in a copy of the map as of the
    // time when the task became available.  It's possible that in the meantime (e.g. due to
    // a parallel task) the map has been altered.
    // Map<String, Object> inputVarsMap = (Map<String, Object>)
    // inputMap.get(TASK_INPUT_VARIABLES_NAME);

    // So, instead, we get the current values directly from the process instance, rather than the
    // values copied into the task

    Long processInstanceId = task.getTaskData().getProcessInstanceId();
    Map<String, Object> inputVarsMap = null;

    try {
      WorkflowProcessInstance pi =
          (WorkflowProcessInstance) kSession.getProcessInstance(processInstanceId);
      inputVarsMap = (Map<String, Object>) pi.getVariable(VARIABLES_PROPERTY);
    } catch (Exception e) {
      // not an active process?  look in the dB.
      log.error(e);
      List<VariableInstanceLog> vars =
          JPAProcessInstanceDbLog.findVariableInstances(processInstanceId, VARIABLES_PROPERTY);
      log.info("variable count: " + vars.size());
      if (vars.size() > 0) {
        // why more than one???
        inputVarsMap = (Map<String, Object>) vars.get(0);
      }
    }

    if (inputVarsMap != null) {
      // initialize the output map with the input values
      log.debug("Copying input map: " + inputVarsMap);
      outputVarsMap.putAll(inputVarsMap);
    }

    if (variables != null && variables.size() > 0) {
      log.debug("Adding variables: " + variables);
      // update with any new or modified values
      outputVarsMap.putAll(variables);
    }

    if (outputVarsMap.size() > 0) {
      log.debug("Adding map to output");
      outputMap.put(TASK_OUTPUT_VARIABLES_NAME, outputVarsMap);
    }

    // start the task
    if (task.getTaskData().getStatus().equals(org.jbpm.task.Status.Reserved)) {
      BlockingTaskOperationResponseHandler operationResponseHandler =
          new BlockingTaskOperationResponseHandler();
      // change status to InProgress
      taskClient.start(id, assignee, operationResponseHandler);
    }

    // kSession.getWorkItemManager().completeWorkItem(task.getTaskData().getWorkItemId(), new
    // HashMap<String,Object>());
    BlockingTaskOperationResponseHandler taskResponseHandler =
        new BlockingTaskOperationResponseHandler();
    // TODO: since we're passing the variables map further down, maybe we don't need to pass it
    // here?  Test this.
    ContentData contentData = ContentMarshallerHelper.marshal(outputMap, null);
    taskClient.complete(id, assignee, contentData, taskResponseHandler);
    taskResponseHandler.waitTillDone(1000);

    // note that we have to pass the variables again.
    kSession.getWorkItemManager().completeWorkItem(task.getTaskData().getWorkItemId(), outputMap);

    // update completed date
    // For some reason this does not get updated by default, and
    // there appears to be no JBPM API way to do this!
    org.jbpm.task.Task t = taskRepo.findOne(task.getId());
    t.getTaskData().setCompletedOn(new Date());
    // update the user
    t.getTaskData().setActualOwner(new User(assignee));

    // note that JPA handles updating of this object automatically

  }