/** Makes a deep copy of this object. */
  protected Object clone() {

    CombinationTasksToFire clone = new CombinationTasksToFire();

    clone.setTokens(this.getNumberMissingTokens());
    clone.setTasks(this.getTasks().deepCopy());

    return clone;
  }
  /*
   * Checks if a element is enabled at the current marking. <p> <b>Note:</b>
   * The element MUST be in the Element.
   *
   * @param element element/transition to check
   *
   * @return true if the element is enabled at the current marking. false
   * otherwise.
   */
  public boolean isEnabled(int element) {
    if (hNet.getInputSet(element) == null) {
      return false;
    }

    if (hNet.getInputSet(element).size() == 0) {
      if (startPlace < 1) {
        return false;
      }
    } else {
      bestCombination = findBestSetTasks(element);
      if (bestCombination.getNumberMissingTokens() > 0) {
        return false;
      }
    }

    return true;
  }
  private CombinationTasksToFire findBestSetTasks(int element) {
    CombinationTasksToFire bCombination = null;
    CombinationTasksToFire combination = null;
    HNSubSet noTokensFromTasks = null;
    HNSubSet treatedTasks = null;
    HNSet inputSet = null;
    HNSet temp_inputSet = null;

    HNSubSet subset = null;

    int numberMissingTokens = 0;
    int rootTask = ROOT_TASK_ID;

    bCombination = new CombinationTasksToFire();

    inputSet = hNet.getInputSet(element);

    if (inputSet.size() == 0) {
      if (startPlace <= 0) {
        numberMissingTokens++; // one token is missing
      }
    } else {
      // inputSubset is not empty. Search for tasks that "have tokens" to
      // element

      noTokensFromTasks = getTasksWithEmptyOutputPlaces(element);

      // >>>>>>>>>>>>>>>>>> Hint!!! I think that's why I don't run into
      // problems...
      // /// Idea -> shrink the subsets without using a temp variable, get
      // / the size before shrinking, shrink them, reorder the set and
      // /remove the empty set (do this via a method in the class
      // /HNSet, get the new size. This is the number of missing tokens.

      // make a copy to avoid destroying the original net
      inputSet = inputSet.deepCopy();
      temp_inputSet = new HNSet();
      // removing the tasks whose output subsets that contain element are
      // empty
      for (int iInputSubsets = 0; iInputSubsets < inputSet.size(); iInputSubsets++) {
        subset = inputSet.get(iInputSubsets);
        subset.removeAll(noTokensFromTasks);
        if (subset.size() == 0) {
          numberMissingTokens += 1;
        } else {
          temp_inputSet.add(subset);
        }
      }
      inputSet = temp_inputSet;
      // retrieving the best combination of tasks that can fire to enable
      // element

      if (inputSet.size() > 0) {
        combination = new CombinationTasksToFire();
        treatedTasks = new HNSubSet();
        bCombination =
            findBestCombination(bCombination, inputSet, rootTask, combination, treatedTasks);
      }
    }

    bCombination.setElementToFire(element);
    bCombination.setTokens(bCombination.getNumberMissingTokens() + numberMissingTokens);

    return bCombination;
  }
  private CombinationTasksToFire findBestCombination(
      CombinationTasksToFire bCombination,
      HNSet inputSet,
      int rootTask,
      CombinationTasksToFire combination,
      HNSubSet treatedTasks) {

    int task = -1;
    HNSet alreadyMarkedPlaces = null;
    HNSet temp_inputSet = null;
    HNSubSet noTokensFromTasks = null;
    HNSubSet subset = null;

    if ((bCombination.getTasks().size() == 0)
        || (bCombination.getNumberMissingTokens() > combination.getNumberMissingTokens())) {
      if (rootTask != ROOT_TASK_ID) {
        alreadyMarkedPlaces = getAlreadyMarkedPlaces(inputSet, rootTask);
        noTokensFromTasks = HNSet.getUnionSet(alreadyMarkedPlaces);
        inputSet.removeAll(alreadyMarkedPlaces);
        combination.getTasks().add(rootTask);
      }

      if (inputSet.size() == 0) {
        bCombination = combination.copy();
      } else {

        // akam: I stoppe here - 10/07/2005
        if (rootTask != ROOT_TASK_ID) {
          temp_inputSet = new HNSet();
          for (int iInputSet = 0; iInputSet < inputSet.size(); iInputSet++) {
            subset = inputSet.get(iInputSet);
            subset.removeAll(noTokensFromTasks);
            subset.removeAll(treatedTasks);
            if (subset.size() == 0) {
              combination.setTokens(combination.getNumberMissingTokens() + 1);
            } else {
              temp_inputSet.add(subset);
            }
          }
          inputSet = temp_inputSet;
        }

        for (int iInputSet = 0; iInputSet < inputSet.size(); iInputSet++) {
          subset = inputSet.get(iInputSet);
          while (subset.size() > 0) {
            task = subset.get(generator.nextInt(subset.size()));
            bCombination =
                findBestCombination(
                    bCombination,
                    inputSet.deepCopy(),
                    task,
                    combination.copy(),
                    treatedTasks.deepCopy());
            treatedTasks.add(task);
            subset.remove(task);
          }
        }
      }
    }

    return bCombination;
  }
  /**
   * Fires a element even if it is not enabled. When the element has duplicates, it looks ahead to
   * set which duplicate to fire.
   *
   * <p><b>Note:</b> The element MUST be in the net.
   *
   * @param element element to be fired.
   * @param pi process instance where the element to be fired is.
   * @param elementPositionInPi element position.
   * @return int number of tokens that needed to be added to fire this element.
   */
  public int fire(int element, ProcessInstance pi, int elementPositionInPi) {

    int addedTokens = 0;
    int elementDuplicates;

    if ((hNet.getReverseDuplicatesMapping()[element]).size() == 1) {
      elementDuplicates = hNet.getReverseDuplicatesMapping()[element].get(0);
    } else {
      // identify which duplicate to fire
      HNSubSet duplicates = hNet.getReverseDuplicatesMapping()[element].deepCopy();

      // getting the duplicates that are enabled
      for (int i = 0; i < duplicates.size(); i++) {

        if (!isEnabled(duplicates.get(i))) {
          duplicates.remove(duplicates.get(i));
        }
      }

      if (duplicates.size() > 0) {
        if (duplicates.size() == 1) {
          elementDuplicates = duplicates.get(0);
        } else {
          // getting the output tasks of the duplicates. These output
          // are used to
          // look ahead at the process instance
          HNSubSet unionMappedToATEsCode = getAllOutputElementsOfDuplicates(duplicates);
          AuditTrailEntryList ATEntriesList = pi.getAuditTrailEntryList();
          // advancing the pointer in the ATEntries till the current
          // element + 1

          AuditTrailEntry ATEntry;
          int elementInATE = -1;
          for (int i = elementPositionInPi + 1; i < ATEntriesList.size(); i++) {
            try {
              ATEntry = ATEntriesList.get(i);
              elementInATE =
                  this.hNet
                      .getLogEvents()
                      .findLogEventNumber(ATEntry.getElement(), ATEntry.getType());
              if (unionMappedToATEsCode.contains(elementInATE)) {
                break;
              }

            } catch (IOException ex) {
              break;
            } catch (IndexOutOfBoundsException ex) {
              break;
            }
          }
          elementDuplicates = identifyDuplicateToFire(duplicates, elementInATE);
        }
      } else {
        // because no duplicate is enabled, a random one is chosen to
        // fire...
        elementDuplicates =
            (hNet.getReverseDuplicatesMapping()[element])
                .get(generator.nextInt(hNet.getReverseDuplicatesMapping()[element].size()));
      }
    }

    bestCombination = findBestSetTasks(elementDuplicates);
    addedTokens += bestCombination.getNumberMissingTokens();
    removeTokensOutputPlaces(elementDuplicates, bestCombination.getTasks());
    addTokensOutputPlaces(elementDuplicates);
    addToPossiblyEnabledElements(elementDuplicates);

    // registering the firing of element...
    hNet.increaseElementActualFiring(
        elementDuplicates,
        MethodsForWorkflowLogDataStructures.getNumberSimilarProcessInstances(pi));
    // updating the arc usage for the individual...
    hNet.increaseArcUsage(
        bestCombination.getElementToFire(),
        bestCombination.getTasks(),
        MethodsForWorkflowLogDataStructures.getNumberSimilarProcessInstances(pi));

    return addedTokens;
  }