/*
   * (non-Javadoc)
   *
   * @see com.htm.TaskParentInterface#exit(java.lang.String)
   */
  public void exit(String tiid) throws HumanTaskManagerException {
    try {
      /* Start transaction for creating a task instance */
      dataAccessProvider.beginTx();
      log.debug("Exit task instance - Trying to exit task instance '" + tiid + "'");
      this.authorizationManager.authorizeTaskParentAction(getCurrentUser(), tiid, EActions.EXIT);
      ITaskInstance taskInstance = dataAccessProvider.getTaskInstance(tiid);
      String oldState = taskInstance.getStatus().toString();

      // TODO for skipped, completed, faulted task instances an individual
      // error message should
      // be created. Currently only an invalidarguemntexception is thrown
      if (taskInstance.isExpired()) {
        String errorMsg =
            "The task instance '"
                + taskInstance.getId()
                + "' can not be exited "
                + "because it expired on "
                + Utilities.formatTimestamp(taskInstance.getExpirationTime());
        log.error(errorMsg);
        throw new InvalidOperationException(errorMsg);
      }

      taskInstance.setStatus(ETaskInstanceState.EXITED);

      // Audit
      if (Configuration.isLoggingEnabled()) {
        IAuditLogger auditLogger = AuditFactory.newInstance();
        TaskInstanceView taskInstanceView = new TaskInstanceView(taskInstance);
        AuditAction action =
            new AuditAction(
                EActions.EXIT.toString(),
                taskInstanceView,
                taskInstanceView.getStatus(),
                oldState,
                SessionUtils.getCurrentUser());
        auditLogger.logAction(action);
      }
      dataAccessProvider.commitTx();
    } catch (HumanTaskManagerException e) {
      dataAccessProvider.rollbackTx();
      throw e;

    } finally {
      dataAccessProvider.close();
    }
  }
/**
 * The implementation of the task parent interface.
 *
 * @author Sebastian Wagner
 * @author Tobias Unger
 */
@Transactional
public class TaskParentInterfaceImpl implements TaskParentInterface {

  @Autowired protected IDataAccessProvider dataAccessProvider;

  protected EventHandler evenHandler;

  protected Logger log = Utilities.getLogger(this.getClass());

  @Autowired private TaskInstanceFactory taskInstanceFactory;

  @Autowired private WorkItemFactory workItemFactory;

  @Autowired private IAuthorizationManager authorizationManager;

  /**
   * Creates a new {@link TaskParentInterfaceImpl} object.</b> It initializes the {@link
   * IDataAccessProvider}</b> Moreover all timers are reactivated (e.g. suspend until timer,
   * expiration timer). See {@link TaskInstanceTimers} for more information.
   */
  public TaskParentInterfaceImpl() {
    // this.dataAccessProvider = IDataAccessProvider.Factory.newInstance();
    this.evenHandler = EventHandler.newInstance();

    /*
     * Reactivate all task instance timers (e.g. suspend until timer,
     * expiration timer) i.e. schedule the execution of certain actions
     * until their timer has expired.
     */
    TaskInstanceTimers.reactivateTimers();
  }

  /*
   * (non-Javadoc)
   *
   * @see com.htm.TaskParentInterface#createTaskInstance(java.lang.String,
   * java.lang.String, java.lang.Object, java.util.Set, java.lang.String,
   * java.sql.Timestamp)
   */
  public String createTaskInstance(
      String taskParentId,
      Set<ICorrelationProperty> correlationProperties,
      String taskModelName,
      String taskInstanceName,
      Object inputData,
      Set<IAttachment> attachments,
      Timestamp expirationTime)
      throws HumanTaskManagerException {

    try {

      // TODO contextId
      String initiatorUserId = getCurrentUser();

      if (StringUtils.isEmpty(initiatorUserId)) {
        String errorMsg = "The username of the task initiator could not be determined.";
        log.error(errorMsg);
        throw new HumanTaskManagerException(errorMsg);
      }

      /* Start transaction for creating a task instance */
      dataAccessProvider.beginTx();
      this.authorizationManager.authorizeTaskParentAction(
          initiatorUserId, null, EActions.CREATE_TASK_INSTANCE);

      /* Create the task instance model */
      TaskInstanceFactory taskFac = this.taskInstanceFactory;
      ITaskInstance taskInstance =
          taskFac.createTaskInstance(
              taskModelName,
              taskInstanceName,
              inputData,
              taskParentId,
              correlationProperties,
              expirationTime);

      /* Store the task instance instance */
      dataAccessProvider.persistHumanTaskInstance(taskInstance);

      // Audit
      // if (Configuration.isLoggingEnabled()) {
      // IAuditLogger auditLogger = AuditFactory.newInstance();
      // TaskInstanceView taskInstanceView = new
      // TaskInstanceView(taskInstance);
      // AuditAction action = new
      // AuditAction(EActions.CREATE_TASK_INSTANCE.toString(),
      // taskInstanceView, taskInstanceView.getStatus(), null,
      // initiatorUserId);
      // auditLogger.logAction(action);
      // }

      /* Activate the task instance expiration timer. */
      TaskInstanceTimers.activateExpirationTimer(taskInstance);

      /*
       * Create Work Items and add attachments
       */
      List<IWorkItem> workItems = new ArrayList<IWorkItem>();

      WorkItemFactory workItemFac = this.workItemFactory;
      /*
       * A single work item for the task initiator has to be created
       * because she is not defined in the task model by a people query.
       * This should be done before the other work items are created since
       * the people queries in the task model could refer on the task
       * initiator.
       */
      workItems.add(
          workItemFac.createNewWorkItem(initiatorUserId, EHumanRoles.TASK_INITIATOR, taskInstance));
      /*
       * The attachments should also be added before the other work items
       * are created then the people queries can refer on the attachments.
       */
      taskInstance.setAttachments(
          this.taskInstanceFactory.createAssignedUser(initiatorUserId), attachments);

      /*
       * Create work items for roles that are defined via people queries
       * within the task model i.e. for roles business administrators,
       * task stakeholder, potential owners
       */
      workItems.addAll(workItemFac.createWorkItemsForTaskInstance(taskInstance));

      // TODO: evaluate QueryProperties
      taskFac.evaluateQueryProperties(taskInstance.getId(), taskModelName);

      /*
       * If the task instance has one or more potential owners it
       * transitions to ready state i.e. it is activated
       */
      boolean hasPotentialOwner = taskInstance.hasPotentialOwners();

      log.debug("Create task instance - Task instance has potential owners : " + hasPotentialOwner);
      /*
       * The task instance can only set to the READY state if it has
       * potential owners and if the task instance is not already expired.
       */
      String oldState = taskInstance.getStatus().toString();
      if (taskInstance.hasPotentialOwners() && !taskInstance.isExpired()) {
        taskInstance.setStatus(ETaskInstanceState.READY);
        taskInstance.setActivationTime(new Timestamp(Calendar.getInstance().getTimeInMillis()));
        // TODO only one potential owner -> immediately go to reserved
        // state
      }

      if (!hasPotentialOwner) {
        log.info(
            "Create task instance - "
                + "Can't transition to '"
                + ETaskInstanceState.READY
                + "' state because no potential owner(s) were found.");
      }
      if (taskInstance.isExpired()) {
        log.info(
            "Create task instance - "
                + "The task instance is "
                + ETaskInstanceState.OBSOLETE
                + " created because it has already expired at '"
                + Utilities.formatTimestamp(expirationTime)
                + "'.");
      }

      dataAccessProvider.persistWorkItems(workItems);
      /* Inform event subscriber about the new work items */
      publishNewWorkItemEvent(workItems);

      // Audit
      if (Configuration.isLoggingEnabled()) {
        IAuditLogger auditLogger = AuditFactory.newInstance();
        TaskInstanceView taskInstanceView = new TaskInstanceView(taskInstance);
        AuditAction action =
            new AuditAction(
                EActions.CREATE_TASK_INSTANCE.toString(),
                taskInstanceView,
                taskInstanceView.getStatus(),
                oldState,
                initiatorUserId);
        auditLogger.logAction(action);
      }

      dataAccessProvider.commitTx();
      return taskInstance.getId();
    } catch (HumanTaskManagerException e) {
      dataAccessProvider.rollbackTx();
      throw e;
    } finally {
      dataAccessProvider.close();
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.htm.TaskParentInterface#exit(java.lang.String)
   */
  public void exit(String tiid) throws HumanTaskManagerException {
    try {
      /* Start transaction for creating a task instance */
      dataAccessProvider.beginTx();
      log.debug("Exit task instance - Trying to exit task instance '" + tiid + "'");
      this.authorizationManager.authorizeTaskParentAction(getCurrentUser(), tiid, EActions.EXIT);
      ITaskInstance taskInstance = dataAccessProvider.getTaskInstance(tiid);
      String oldState = taskInstance.getStatus().toString();

      // TODO for skipped, completed, faulted task instances an individual
      // error message should
      // be created. Currently only an invalidarguemntexception is thrown
      if (taskInstance.isExpired()) {
        String errorMsg =
            "The task instance '"
                + taskInstance.getId()
                + "' can not be exited "
                + "because it expired on "
                + Utilities.formatTimestamp(taskInstance.getExpirationTime());
        log.error(errorMsg);
        throw new InvalidOperationException(errorMsg);
      }

      taskInstance.setStatus(ETaskInstanceState.EXITED);

      // Audit
      if (Configuration.isLoggingEnabled()) {
        IAuditLogger auditLogger = AuditFactory.newInstance();
        TaskInstanceView taskInstanceView = new TaskInstanceView(taskInstance);
        AuditAction action =
            new AuditAction(
                EActions.EXIT.toString(),
                taskInstanceView,
                taskInstanceView.getStatus(),
                oldState,
                SessionUtils.getCurrentUser());
        auditLogger.logAction(action);
      }
      dataAccessProvider.commitTx();
    } catch (HumanTaskManagerException e) {
      dataAccessProvider.rollbackTx();
      throw e;

    } finally {
      dataAccessProvider.close();
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.htm.ITaskManagerLogin#login(java.lang.String, java.lang.String)
   */
  @Deprecated
  public String login(String userid, String password) throws AuthenticationException {
    return null;
  }

  protected String getCurrentUser() throws UserException {
    return SessionUtils.getCurrentUser();
  }

  protected void publishNewWorkItemEvent(List<IWorkItem> workItems) {
    if (workItems != null) {
      Iterator<IWorkItem> iter = workItems.iterator();
      /*
       * Create an event for each work item that was created and inform
       * the subscribers about it
       */
      while (iter.hasNext()) {
        evenHandler.notifySubscribers(
            new CreateWorkItemEvent(new WorkItemView((IWorkItem) iter.next())));
      }
    }
  }
}
  /*
   * (non-Javadoc)
   *
   * @see com.htm.TaskParentInterface#createTaskInstance(java.lang.String,
   * java.lang.String, java.lang.Object, java.util.Set, java.lang.String,
   * java.sql.Timestamp)
   */
  public String createTaskInstance(
      String taskParentId,
      Set<ICorrelationProperty> correlationProperties,
      String taskModelName,
      String taskInstanceName,
      Object inputData,
      Set<IAttachment> attachments,
      Timestamp expirationTime)
      throws HumanTaskManagerException {

    try {

      // TODO contextId
      String initiatorUserId = getCurrentUser();

      if (StringUtils.isEmpty(initiatorUserId)) {
        String errorMsg = "The username of the task initiator could not be determined.";
        log.error(errorMsg);
        throw new HumanTaskManagerException(errorMsg);
      }

      /* Start transaction for creating a task instance */
      dataAccessProvider.beginTx();
      this.authorizationManager.authorizeTaskParentAction(
          initiatorUserId, null, EActions.CREATE_TASK_INSTANCE);

      /* Create the task instance model */
      TaskInstanceFactory taskFac = this.taskInstanceFactory;
      ITaskInstance taskInstance =
          taskFac.createTaskInstance(
              taskModelName,
              taskInstanceName,
              inputData,
              taskParentId,
              correlationProperties,
              expirationTime);

      /* Store the task instance instance */
      dataAccessProvider.persistHumanTaskInstance(taskInstance);

      // Audit
      // if (Configuration.isLoggingEnabled()) {
      // IAuditLogger auditLogger = AuditFactory.newInstance();
      // TaskInstanceView taskInstanceView = new
      // TaskInstanceView(taskInstance);
      // AuditAction action = new
      // AuditAction(EActions.CREATE_TASK_INSTANCE.toString(),
      // taskInstanceView, taskInstanceView.getStatus(), null,
      // initiatorUserId);
      // auditLogger.logAction(action);
      // }

      /* Activate the task instance expiration timer. */
      TaskInstanceTimers.activateExpirationTimer(taskInstance);

      /*
       * Create Work Items and add attachments
       */
      List<IWorkItem> workItems = new ArrayList<IWorkItem>();

      WorkItemFactory workItemFac = this.workItemFactory;
      /*
       * A single work item for the task initiator has to be created
       * because she is not defined in the task model by a people query.
       * This should be done before the other work items are created since
       * the people queries in the task model could refer on the task
       * initiator.
       */
      workItems.add(
          workItemFac.createNewWorkItem(initiatorUserId, EHumanRoles.TASK_INITIATOR, taskInstance));
      /*
       * The attachments should also be added before the other work items
       * are created then the people queries can refer on the attachments.
       */
      taskInstance.setAttachments(
          this.taskInstanceFactory.createAssignedUser(initiatorUserId), attachments);

      /*
       * Create work items for roles that are defined via people queries
       * within the task model i.e. for roles business administrators,
       * task stakeholder, potential owners
       */
      workItems.addAll(workItemFac.createWorkItemsForTaskInstance(taskInstance));

      // TODO: evaluate QueryProperties
      taskFac.evaluateQueryProperties(taskInstance.getId(), taskModelName);

      /*
       * If the task instance has one or more potential owners it
       * transitions to ready state i.e. it is activated
       */
      boolean hasPotentialOwner = taskInstance.hasPotentialOwners();

      log.debug("Create task instance - Task instance has potential owners : " + hasPotentialOwner);
      /*
       * The task instance can only set to the READY state if it has
       * potential owners and if the task instance is not already expired.
       */
      String oldState = taskInstance.getStatus().toString();
      if (taskInstance.hasPotentialOwners() && !taskInstance.isExpired()) {
        taskInstance.setStatus(ETaskInstanceState.READY);
        taskInstance.setActivationTime(new Timestamp(Calendar.getInstance().getTimeInMillis()));
        // TODO only one potential owner -> immediately go to reserved
        // state
      }

      if (!hasPotentialOwner) {
        log.info(
            "Create task instance - "
                + "Can't transition to '"
                + ETaskInstanceState.READY
                + "' state because no potential owner(s) were found.");
      }
      if (taskInstance.isExpired()) {
        log.info(
            "Create task instance - "
                + "The task instance is "
                + ETaskInstanceState.OBSOLETE
                + " created because it has already expired at '"
                + Utilities.formatTimestamp(expirationTime)
                + "'.");
      }

      dataAccessProvider.persistWorkItems(workItems);
      /* Inform event subscriber about the new work items */
      publishNewWorkItemEvent(workItems);

      // Audit
      if (Configuration.isLoggingEnabled()) {
        IAuditLogger auditLogger = AuditFactory.newInstance();
        TaskInstanceView taskInstanceView = new TaskInstanceView(taskInstance);
        AuditAction action =
            new AuditAction(
                EActions.CREATE_TASK_INSTANCE.toString(),
                taskInstanceView,
                taskInstanceView.getStatus(),
                oldState,
                initiatorUserId);
        auditLogger.logAction(action);
      }

      dataAccessProvider.commitTx();
      return taskInstance.getId();
    } catch (HumanTaskManagerException e) {
      dataAccessProvider.rollbackTx();
      throw e;
    } finally {
      dataAccessProvider.close();
    }
  }
@Ignore
public class StructureTestTask {
  EOperations state;
  int strNr = 0;
  boolean locked = false;
  boolean controlled = false;
  StructureTestTask parentTask = null;
  StructureTestTask mergeTask = null;
  List<StructureTestTask> subTasks = new ArrayList<StructureTestTask>();
  List<StructureTestTask> controlledTasks = new ArrayList<StructureTestTask>();

  private Logger log = Utilities.getLogger(this.getClass());

  public StructureTestTask() {}

  public StructureTestTask(boolean locked) {
    this.locked = locked;
  }

  public void setLock(boolean locked) {
    this.locked = locked;
  }

  public void setControll(boolean controlled) {
    this.controlled = controlled;
  }

  public void addSubTask(StructureTestTask subTask) {
    subTask.setParentTask(this);

    int max = 0;
    for (StructureTestTask childTask : this.getSubTasks()) {
      if (childTask.getStrNr() > max) {
        max = childTask.getStrNr();
      }
    }

    subTask.setStrNr(max + 1);
    this.subTasks.add(subTask);
  }

  public StructureTestTask getMergeTaskCopy() {
    StructureTestTask copy = new StructureTestTask();
    for (StructureTestTask controlledTask : this.getControlledTasks()) {
      copy.addControlledTask(controlledTask.getMergeTaskCopy());
    }
    return copy;
  }

  public void removeSubTask(StructureTestTask subTask) {
    subTask.setParentTask(null);
    this.subTasks.remove(subTask);
  }

  public void removeControlledTask(StructureTestTask controlledTask) {
    controlledTask.setMergeTask(null);
    controlledTask.setControll(false);
    this.controlledTasks.remove(controlledTask);
  }

  public void addControlledTask(StructureTestTask controlledTask) {
    controlledTask.setMergeTask(this);
    controlledTask.setControll(true);
    this.controlledTasks.add(controlledTask);
  }

  public List<StructureTestTask> getSubTasks() {
    return this.subTasks;
  }

  public List<StructureTestTask> getControlledTasks() {
    return this.controlledTasks;
  }

  private void setParentTask(StructureTestTask parentTask) {
    this.parentTask = parentTask;
  }

  private void setMergeTask(StructureTestTask mergeTask) {
    this.mergeTask = mergeTask;
  }

  public int getStrNr() {
    return this.strNr;
  }

  public void setStrNr(int strNr) {
    this.strNr = strNr;
  }

  public static int getTotalMergeTaskCount(String tiid, IStructuredTaskClientInterface stci)
      throws HumanTaskManagerException {
    List<String> controlledTasks = stci.getControlledTasks(tiid);
    int count = controlledTasks.size();
    for (String controlledTask : controlledTasks) {
      count = count + getTotalMergeTaskCount(controlledTask, stci);
    }
    return count;
  }

  public static int getTotalTestMergeTaskCount(
      StructureTestTask task, IStructuredTaskClientInterface stci) {
    List<StructureTestTask> controlledTasks = task.getControlledTasks();
    int count = controlledTasks.size();
    for (StructureTestTask controlledTask : controlledTasks) {
      count = count + getTotalTestMergeTaskCount(controlledTask, stci);
    }
    return count;
  }

  public boolean verifyStructure(String tiid, IStructuredTaskClientInterface stci)
      throws HumanTaskManagerException {
    log.info("Verify Structure of Task " + tiid);

    StructuredTaskInstanceView task = stci.getStructuredTaskInfo(tiid);

    if (task.isLocked() != this.locked) {
      log.warn("Locking state of Task " + tiid + " is not correct (" + this.locked + ")");
      return false;
    }

    if (task.isControlled() != this.controlled) {
      log.warn("Controll state of Task " + tiid + " is not correct (" + this.controlled + ")");
      return false;
    }

    log.info("Locked and Controll state of Task " + tiid + " is correct");

    if (getTotalMergeTaskCount(tiid, stci) != getTotalTestMergeTaskCount(this, stci)) {
      log.info("!!!Merge Task size is not correct!!!");
      return false;
    }
    log.info(
        "Total merge task size of Task "
            + tiid
            + " correct ("
            + getTotalTestMergeTaskCount(this, stci)
            + ")");

    List<String> subTasks = stci.getSubTasks(tiid);

    if (subTasks.size() == this.getSubTasks().size()) {
      for (StructureTestTask testSubTask : this.getSubTasks()) {
        String subTask = stci.getSubTaskByStructureNr(tiid, testSubTask.getStrNr());

        log.info("Sub Task " + subTask + " correctly found");
        if (!testSubTask.verifyStructure(subTask, stci)) {
          return false;
        }
      }
    } else {
      log.info("!!!Sub Task size is not correct!!!");
      return false;
    }

    return true;
  }
}