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

import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
import static org.apache.commons.lang3.StringUtils.upperCase;

import java.util.Comparator;

import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehler;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehlerart;
import de.ivu.elect.util.CaseAndDiacriticIgnoringComparator;
import de.ivu.elect.util.NullComparator;

public class StimmeingabefehlerComparator implements Comparator<Stimmeingabefehler> {

  private static final Comparator<Object> NULL_COMPARATOR = NullComparator.NULL_BEFORE_NON_NULL_COMPARATOR;

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

  private static final String CUSTOM_FEHLERSCHLUESSEL_MUSS_INFIX = "Muss";

  private static final String CUSTOM_FEHLERSCHLUESSEL_KANN_INFIX = "Kann";

  public StimmeingabefehlerComparator() {
    super();
  }

  @Override
  public int compare(Stimmeingabefehler one, Stimmeingabefehler another) {
    if (one == null || another == null) {
      return NULL_COMPARATOR.compare(one, another);
    }

    int comparedByStimmeingabefehlerart = compareByStimmeingabefehlerart(one, another);

    if (comparedByStimmeingabefehlerart == 0) {
      return compareByBeschreibung(one, another);
    }

    return comparedByStimmeingabefehlerart;
  }

  private int compareByStimmeingabefehlerart(Stimmeingabefehler one, Stimmeingabefehler another) {
    return compareByStimmeingabefehlerart(one.getFehlerart(), another.getFehlerart());
  }

  private int compareByBeschreibung(Stimmeingabefehler one, Stimmeingabefehler another) {
    return BESCHREIBUNG_COMPARATOR.compare(one.getBeschreibung(), another.getBeschreibung());
  }

  public static int compareByStimmeingabefehlerart(Stimmeingabefehlerart one, Stimmeingabefehlerart another) {
    if (one.isError()) {
      if (another.isError()) {
        return 0;
      }

      return -1;
    }

    if (another.isError()) {
      return 1;
    }

    return one.compareTo(another);
  }

  public static int compareByFehlerschluessel(String one, String another) {
    return normalizeFehlerschluessel(one).compareTo(normalizeFehlerschluessel(another));
  }

  public static int compareByHavingCustomFehlerschluessel(String one, String another) {
    if (containsCustomMussFehlerschluessel(one)) {
      if (containsCustomMussFehlerschluessel(another)) {
        return normalizeFehlerschluessel(one).compareTo(normalizeFehlerschluessel(another));
      }

      return -1;
    }

    if (containsCustomKannFehlerschluessel(one)) {
      if (containsCustomMussFehlerschluessel(another)) {
        return 1;
      }

      if (containsCustomKannFehlerschluessel(another)) {
        return normalizeFehlerschluessel(one).compareTo(normalizeFehlerschluessel(another));
      }

      return -1;
    }

    if (containsCustomMussFehlerschluessel(another) || containsCustomKannFehlerschluessel(another)) {
      return 1;
    }

    return 0;
  }

  private static boolean containsCustomKannFehlerschluessel(String value) {
    return containsCustomFehlerschluesselInfix(value, CUSTOM_FEHLERSCHLUESSEL_KANN_INFIX);
  }

  private static boolean containsCustomMussFehlerschluessel(String value) {
    return containsCustomFehlerschluesselInfix(value, CUSTOM_FEHLERSCHLUESSEL_MUSS_INFIX);
  }

  private static boolean containsCustomFehlerschluesselInfix(String value, String infix) {
    return containsIgnoreCase(value, infix);
  }

  private static String normalizeFehlerschluessel(String value) {
    String result = trimToEmpty(value);
    result = upperCase(result);
    result = result.replaceAll("(\\d+)\\s+", "$1A "); // 11 MUSS > 11A MUSS, da es bspw. 12a Muss, 12b Muss bei der LTW gibt...
    result = leftPad(result, 15, "0");

    return result;
  }
}
