package gov.cms.grouper.snf.model.table;

import gov.cms.grouper.snf.lego.SnfComparator;
import gov.cms.grouper.snf.lego.SnfUtils;
import java.time.LocalDate;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;

public abstract class BasicRow {

  public static final BiFunction<BasicRow, Integer, Boolean> versionSelector = (row,
      check) -> SnfComparator.betweenInclusive(row.getLowVersion(), check, row.getHighVersion());

  public static final BiConsumer<BasicRow, String> lowVersionParser =
      (val, str) -> val.setLowVersion(SnfUtils.parse(str, null));

  public static final BiConsumer<BasicRow, String> highVersionParser =
      (val, str) -> val.setHighVersion(SnfUtils.parse(str, null));

  private Integer lowVersion;
  private Integer highVersion;
  private LocalDate from;
  private LocalDate to;

  protected BasicRow() {
    this(null, null, null, null);
  }

  protected BasicRow(Integer lowVersion, Integer highVersion, LocalDate from, LocalDate to) {
    super();
    this.lowVersion = lowVersion;
    this.highVersion = highVersion;
    this.from = from;
    this.to = to;
  }

  public LocalDate getFrom() {
    return this.from;
  }

  protected void setFrom(LocalDate from) {
    this.from = from;
  }

  public LocalDate getTo() {
    return this.to;
  }

  protected void setTo(LocalDate to) {
    this.to = to;
  }

  public Integer getLowVersion() {
    return this.lowVersion;
  }

  protected void setLowVersion(Integer lowVersion) {
    this.lowVersion = lowVersion;
  }

  public Integer getHighVersion() {
    return this.highVersion;
  }

  protected void setHighVersion(Integer highVersion) {
    this.highVersion = highVersion;
  }

  /**
   * compute hashcode of Basic row.
   *
   * @param result running result of hashcode
   * @param prime the prime that is being used
   * @return hashCode
   */
  public int getHashCode(int result, final int prime) {
    result =
        prime * result + ((this.getLowVersion() == null) ? 0 : this.getLowVersion().hashCode());
    return result;
  }

  /**
   * Basic functionality to populate the model with effective and termination date for that entry.
   *
   * @param map of version to row
   */
  public void populateVersion(Map<Integer, SnfVersionRow> map) {
    SnfVersionRow low = map.get(this.getLowVersion());
    LocalDate from = low.getFrom();

    SnfVersionRow high = map.get(this.getHighVersion());
    LocalDate to = SnfUtils.nullCheck2(high, null, () -> high.getTo());

    this.setFrom(from);
    this.setTo(to);
  }

  public static <T extends BasicRow> BiFunction<T, Integer, Boolean> getVersionSelector() {
    return SnfUtils.cast(BasicRow.versionSelector);
  }

  public static <T extends BasicRow> BiConsumer<T, String> getLowVersionParser() {
    return SnfUtils.cast(BasicRow.lowVersionParser);
  }

  public static <T extends BasicRow> BiConsumer<T, String> getHighVersionParser() {
    return SnfUtils.cast(BasicRow.highVersionParser);
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = this.getHashCode(result, prime);
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (this.getClass() != obj.getClass()) {
      return false;
    }
    BasicRow other = (BasicRow) obj;
    if (this.lowVersion == null) {
      if (other.lowVersion != null) {
        return false;
      }
    } else if (!this.lowVersion.equals(other.lowVersion)) {
      return false;
    }
    return true;
  }

}
