package gov.cms.grouper.snf.lego;

import gov.cms.grouper.snf.lego.SnfComparator.AcceptNull;
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Function;

/**
 * provide a null safe comparasion when you are force to deal with null
 */
public class Compare<T extends Comparable<T>> {

  private final AcceptNull accept;
  private final BetweenCompare<Integer> between;


  public Compare(AcceptNull accept) {
    super();
    this.accept = accept;
    this.between = BetweenCompare.of(this.accept, this.accept);
  }

  private boolean compute(Comparator<T> comparator, T thisValue, T against,
      Function<Integer, Boolean> eval) {
    boolean checkNull = this.accept.checkNull(Arrays.asList(thisValue, against));
    boolean result = checkNull && compare(thisValue, against, eval);
    return result;
  }

  private boolean compare(T thisValue, T against, Function<Integer, Boolean> eval) {
    Comparator<T> com = this.accept.getComparator();
    int computeValue = com.compare(thisValue, against);
    boolean result = eval.apply(computeValue);
    return result;
  }

  public boolean lte(T thisValue, T isLessThanThis) {
    boolean result = compute(this.accept.getComparator(), thisValue, isLessThanThis,
        (com) -> this.between.betweenInclusive(-1, com, 0));
    return result;
  }

  public boolean lt(T thisValue, T isLessThanThis) {
    boolean result = compute(this.accept.getComparator(), thisValue, isLessThanThis,
        (com) -> this.between.betweenInclusive(-1, com, -1));
    return result;
  }

  public boolean gte(T thisValue, T isGreaterThanThis) {
    boolean result = compute(this.accept.getComparator(), thisValue, isGreaterThanThis,
        (com) -> this.between.betweenInclusive(0, com, 1));
    return result;
  }

  public boolean gt(T thisValue, T isGreaterThanEqualToThis) {
    boolean result = compute(this.accept.getComparator(), thisValue, isGreaterThanEqualToThis,
        (com) -> this.between.betweenInclusive(1, com, 1));
    return result;
  }

  public boolean eq(T thisValue, T isEqualToThis) {
    boolean result = compute(this.accept.getComparator(), thisValue, isEqualToThis,
        (com) -> this.between.betweenInclusive(0, com, 0));
    return result;
  }

  public static <T extends Comparable<T>> Compare<T> of() {
    return of(AcceptNull.FALSE_ON_NULL);
  }

  public static <T extends Comparable<T>> Compare<T> of(AcceptNull accept) {
    return new Compare<T>(accept);
  }
}
