
package de.ivu.elect.jee.entity;

import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.LoggerFactory;

import de.ivu.elect.jee.entity.tk.EntityTK;
import de.ivu.elect.util.CaseAndDiacriticIgnoringNormalizer;


import static java.util.Objects.nonNull;

public class EntityUtil {

  public static boolean compareObjects(String one, String another) {
    return compareObjects((Object) StringUtils.trimToNull(one), (Object) StringUtils.trimToNull(another));
  }

  public static boolean compareObjects(Object one, Object another) {
    if (one == null && another == null || one != null && one.equals(another)) {
      return true;
    }

    return false;
  }

  public static boolean compareObjectsTestString(Object one, Object another) {
    if (one != null && another != null && one instanceof String && another instanceof String) {
      return compareObjects((String) one, (String) another);
    }
    return compareObjects(one, another);
  }

  /**
   * Benutzt immer die in der Vererbungshierarchie konkretesten EntityTK-Klassen. Im Gegensatz dazu liefern Maps, die mittels dem autogenerierten Entity.getEntityToTKFunction() gebaut wurden, immer
   * TKs mit exakt derselben EntityTK-Klasse.
   */
  public static <E extends Entity< ? , ? >> Map<EntityTK, E> mappeNachKonkretestemTk(Collection<E> entities) {
    Map<EntityTK, E> result = new HashMap<>(entities.size() * 2);
    for (E entity : entities) {
      result.put(entity.getTk(), entity);
    }

    return result;
  }

  // ELECT-5456
  public static Date normalizeDate(Date date) {
    if (date == null) {
      return null;
    }
    return DateUtils.truncate(date, Calendar.DATE);
  }

  public static Date normalizeDateTime(Date dateTime) {
    if (dateTime == null) {
      return null;
    }
    return new Date(dateTime.getTime());
  }

  public static <E> E castTo(Entity entity, Class<E> clazz) {
    return (E) entity.castTo(clazz).getEntity();
  }

  public static String trimToNullOrMaxLength(String value, int maxLength, Long id, Class clazz) {
    return trimToMaxLength(StringUtils.trimToNull(value), maxLength, id, clazz);
  }

  public static String trimToMaxLength(String value, int maxLength, Long id, Class clazz) {
    if (nonNull(value)) {
      StringBuilder result = trimToMaxLength(new StringBuilder(value), maxLength);

      // NOTE ELECT-4089, ELECT-5032.
      int utf8CharacterMaxBytesCount = 4;
      int maxBytesCount = maxLength;

      if (result.length() > maxBytesCount / utf8CharacterMaxBytesCount) {
        int bytesCount = result.toString().getBytes(StandardCharsets.UTF_8).length;

        if (bytesCount > maxBytesCount) {
          LoggerFactory.getLogger(clazz).warn(
              "Entferne Zeichen vom Ende einer Zeichenkette, da nicht alle Bytes gespeichert werden können [entity = " + clazz.getSimpleName() + ", id = " + id
                  + ", bytes = " + bytesCount + ", max bytes = " + maxBytesCount + ", text (gekürzt) = " + StringUtils.abbreviate(value, 256) + "]");

          while (result.toString().getBytes(StandardCharsets.UTF_8).length > maxBytesCount) {
            result = trimToMaxLength(result, result.length() - 1);
          }
        }
      }

      return result.toString();
    }

    return null;
  }

  private static StringBuilder trimToMaxLength(StringBuilder value, int maxLength) {
    if (value.length() > 0) {
      value.setLength(Math.min(value.length(), maxLength));

      if (Character.isHighSurrogate(value.charAt(value.length() - 1))) {
        value.setLength(value.length() - 1);
      }
    }

    return value;
  }

  public static String normalizeCaseAndDiacritics(String value) {
    return CaseAndDiacriticIgnoringNormalizer.DEFAULT_INSTANCE.getStringNormalized(value);
  }
}
