
package de.ivu.elect.business.entschaedigung.control;

import java.math.BigDecimal;
import java.util.Comparator;

import de.ivu.elect.business.entschaedigung.entity.Entschaedigung;
import de.ivu.elect.business.entschaedigung.entity.Entschaedigungsart;
import de.ivu.elect.business.entschaedigung.entity.EntschaedigungsartBerechnungsart;
import de.ivu.elect.business.person.control.EntityMitPersonendatenByNamenComparator;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.util.CaseAndDiacriticIgnoringComparator;
import de.ivu.elect.util.NullComparator;

public class EntschaedigungDefaultComparator implements Comparator<Entschaedigung> {

  private static final Comparator<Object> NULL_DEFAULT_COMPARATOR = NullComparator.NON_NULL_BEFORE_NULL_COMPARATOR;

  private static final Comparator<String> STRING_DEFAULT_COMPARATOR = CaseAndDiacriticIgnoringComparator.DEFAULT_INSTANCE;

  private static final Comparator<Personendaten> PERSONENDATEN_DEFAULT_COMPARATOR = new EntityMitPersonendatenByNamenComparator<>();

  public EntschaedigungDefaultComparator() {
    super();
  }

  @Override
  public int compare(Entschaedigung one, Entschaedigung another) {
    if (one != null && another != null) {
      int comparedByPersonendaten = compareByPersonendaten(one.getPersonendaten(), another.getPersonendaten());

      if (comparedByPersonendaten == 0) {
        int comparedByBerechnungsart = compareByEntschaedigungsart(one.getEntschaedigungsart(), another.getEntschaedigungsart());

        if (comparedByBerechnungsart == 0) {
          int comparedByAnzahlGewaehrt = compareByAnzahlGewaehrt(one.getAnzahl(), another.getAnzahl());

          if (comparedByAnzahlGewaehrt == 0) {
            return compareByEinzelbetrag(one.getEinzelbetrag(), another.getEinzelbetrag());
          }

          return comparedByAnzahlGewaehrt;
        }

        return comparedByBerechnungsart;
      }

      return comparedByPersonendaten;
    }

    return NULL_DEFAULT_COMPARATOR.compare(one, another);
  }

  private int compareByPersonendaten(Personendaten one, Personendaten another) {
    return PERSONENDATEN_DEFAULT_COMPARATOR.compare(one, another);
  }

  private int compareByEntschaedigungsart(Entschaedigungsart one, Entschaedigungsart another) {
    if (one != null && another != null) {
      int comparedByBerechnungsart = compareByBerechnungsart(one.getBerechnungsart(), another.getBerechnungsart());

      if (comparedByBerechnungsart == 0) {
        return STRING_DEFAULT_COMPARATOR.compare(one.getTemplate(), another.getTemplate());
      }

      return comparedByBerechnungsart;
    }

    return NULL_DEFAULT_COMPARATOR.compare(one, another);
  }

  private int compareByBerechnungsart(EntschaedigungsartBerechnungsart one, EntschaedigungsartBerechnungsart another) {
    if (EntschaedigungsartBerechnungsart.PER_EINSATZ.equals(one)) {
      if (EntschaedigungsartBerechnungsart.PER_EINSATZ.equals(another)) {
        return 0;
      }

      return -1;
    }

    if (EntschaedigungsartBerechnungsart.PER_EINSATZ.equals(another)) {
      return 1;
    }

    if (one != null && another != null) {
      return another.ordinal() - one.ordinal();
    }

    return NULL_DEFAULT_COMPARATOR.compare(one, another);
  }

  private int compareByAnzahlGewaehrt(Long one, Long another) {
    if (isNullOrZero(one)) {
      if (isNullOrZero(another)) {
        return 0;
      }

      return 1;
    }

    if (isNullOrZero(another)) {
      return -1;
    }

    return one.compareTo(another);
  }

  private int compareByEinzelbetrag(BigDecimal one, BigDecimal another) {
    if (isNullOrZero(one)) {
      if (isNullOrZero(another)) {
        return 0;
      }

      return 1;
    }

    if (isNullOrZero(another)) {
      return -1;
    }

    return another.compareTo(one);
  }

  private static boolean isNullOrZero(Long value) {
    return isNullOrEquals(value, 0L);
  }

  private static boolean isNullOrZero(BigDecimal value) {
    return isNullOrEquals(value, BigDecimal.ZERO);
  }

  private static <T extends Number> boolean isNullOrEquals(Comparable<T> value, T another) {
    return value == null || value.compareTo(another) == 0;
  }
}
