package org.testng.internal;

import java.util.BitSet;
import org.testng.IResultMap;
import org.testng.ITestContext;

/**
 * |--------------------|---------|-------|------------|------------------------------------------|
 * | FailedWithinSuccess| Skipped | Failed| Status Code| Remarks |
 * |--------------------|---------|-------|------------|------------------------------------------|
 * | 0 | 0 | 0 | 0 | Passed tests | | 0 | 0 | 1 | 1 | Failed tests | | 0 | 1 | 0 | 2 | Skipped tests
 * | | 0 | 1 | 1 | 3 | Skipped/Failed tests | | 1 | 0 | 0 | 4 | FailedWithinSuccess tests | | 1 | 0
 * | 1 | 5 | FailedWithinSuccess/Failed tests | | 1 | 1 | 0 | 6 | FailedWithinSuccess/Skipped tests
 * | | 1 | 1 | 1 | 7 | FailedWithinSuccess/Skipped/Failed tests |
 * |--------------------|---------|-------|------------|------------------------------------------|
 */
public class ExitCode {

  public static final int HAS_NO_TEST = 8;
  private static final int FAILED_WITHIN_SUCCESS = 4;
  public static final int SKIPPED = 2;
  public static final int FAILED = 1;
  private static final int SIZE = 3;

  private final BitSet exitCodeBits;

  ExitCode() {
    this(new BitSet(SIZE));
  }

  public static boolean hasFailureWithinSuccessPercentage(int returnCode) {
    return (returnCode & FAILED_WITHIN_SUCCESS) == FAILED_WITHIN_SUCCESS;
  }

  public static boolean hasSkipped(int returnCode) {
    return (returnCode & SKIPPED) == SKIPPED;
  }

  public static boolean hasFailure(int returnCode) {
    return (returnCode & FAILED) == FAILED;
  }

  public static ExitCode newExitCodeRepresentingFailure() {
    BitSet bitSet = new BitSet(SIZE);
    bitSet.set(0, true);
    bitSet.set(1, false);
    bitSet.set(2, false);

    return new ExitCode(bitSet);
  }

  private ExitCode(BitSet exitCodeBits) {
    this.exitCodeBits = exitCodeBits;
  }

  void computeAndUpdate(ITestContext context) {
    computeAndUpdate(0, context.getFailedTests(), context.getFailedConfigurations());
    computeAndUpdate(1, context.getSkippedTests(), context.getSkippedConfigurations());
    computeAndUpdate(2, context.getFailedButWithinSuccessPercentageTests(), null);
  }

  private void computeAndUpdate(int index, IResultMap testResults, IResultMap configResults) {
    boolean containsResults = testResults.size() != 0;
    if (configResults != null) {
      containsResults = containsResults || configResults.size() != 0;
    }
    if (containsResults) {
      this.exitCodeBits.set(index);
    }
  }

  public boolean hasFailure() {
    return exitCodeBits.get(0);
  }

  public boolean hasSkip() {
    return exitCodeBits.get(1);
  }

  public boolean hasFailureWithinSuccessPercentage() {
    return exitCodeBits.get(2);
  }

  public int getExitCode() {
    int exitCode = 0;
    for (int i = 0; i < exitCodeBits.length(); i++) {
      if (exitCodeBits.get(i)) {
        exitCode = exitCode | (1 << i);
      }
    }

    return exitCode;
  }
}
