
package de.ivu.elect.jee.startup;

import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.jee.protokollierung.ProtokollAktiv;

@ApplicationScoped
public class StartupBootstrap {

  private static final Logger LOG = LoggerFactory.getLogger(StartupBootstrap.class);

  private static final String LINE_SEPARATOR = System.lineSeparator();

  public static final String DEFAULT_CHARSET_MUST_MATCH = "default.charset.must.match";

  public static final String DEFAULT_CHARSET_BOM_USAGE_ENABLED = "default.charset.bom.usage.enabled";

  private static final String DEFAULT_CHARSET_BOM_USAGE_ENABLED_LEGACY = "CsvZeichensatz.CHARSET_BOM_USAGE_ENABLED"; // ELECT-5818

  public static final String DEFAULT_LOCALE_SET_EXPECTED = "default.locale.set.expected";

  public static final String DEFAULT_LOCALE_MUST_MATCH = "default.locale.must.match";

  public static final String DEFAULT_TIMEZONE_SET_EXPECTED = "default.timezone.set.expected";

  public static final String DEFAULT_TIMEZONE_MUST_MATCH = "default.timezone.must.match";

  @Inject
  private transient ProtokollAktiv protokollAktiv; // Instance soll möglichst früh erzeugt werden, um sicherzustellen, dass der Schalter für die Protokollierung korrekt gesetzt ist!

  @Inject
  private transient StartupSystemDefaults startupSystemDefaults;

  private boolean initialized = false;

  public StartupBootstrap() {
    super();
  }

  public void startup() {
    if (isAlreadyInitialized()) {
      return;
    }

    init();
  }

  protected boolean isAlreadyInitialized() {
    if (initialized) {
      return true;
    }

    initialized = true;

    return false;
  }

  protected void init() {
    initProtokollierung();

    initApacheSystemDefaults();

    checkSystemDefaults();
  }

  private void checkSystemDefaults() {
    checkDefaultLocale();
    checkDefaultTimeZone();
    checkDefaultCharset();
  }

  private void checkDefaultCharset() {
    String charsetPropertyName = "file.encoding";

    boolean expectedCharsetBomUsageDisabled = "false".equalsIgnoreCase(System.getProperty(DEFAULT_CHARSET_BOM_USAGE_ENABLED))
        || "false".equalsIgnoreCase(System.getProperty(DEFAULT_CHARSET_BOM_USAGE_ENABLED_LEGACY));
    boolean expectedCharsetBomUsageEnabled = !expectedCharsetBomUsageDisabled && (startupSystemDefaults.isExpectedServerCharsetBomUsageEnabled()
        || "true".equalsIgnoreCase(System.getProperty(DEFAULT_CHARSET_BOM_USAGE_ENABLED)) || "true".equalsIgnoreCase(System.getProperty(DEFAULT_CHARSET_BOM_USAGE_ENABLED_LEGACY)));

    System.setProperty(DEFAULT_CHARSET_BOM_USAGE_ENABLED, Boolean.toString(expectedCharsetBomUsageEnabled));
    LOG.info("Charset gefunden: " + Charset.defaultCharset() + " (BOM" + (expectedCharsetBomUsageEnabled ? "" : " NICHT") + " verwendet)");

    Charset expectedCharset = startupSystemDefaults.getExpectedServerCharset();
    LOG.info("Charset erwartet: " + expectedCharset);

    if (expectedCharset.equals(Charset.defaultCharset())) {
      LOG.info("Charset stimmt überein");
    } else {
      String errorMsgPrefix = "Charset STIMMT NICHT ÜBEREIN, änderbar NUR VOR START DES SERVERS mit Property " + createProperty(charsetPropertyName, expectedCharset);

      if ("true".equalsIgnoreCase(System.getProperty(DEFAULT_CHARSET_MUST_MATCH, "false"))) {
        LOG.error(errorMsgPrefix + ", Start ABGEBROCHEN, deaktivierbar mit Property " + createProperty(DEFAULT_CHARSET_MUST_MATCH, false));
        throw new RuntimeException("Charset STIMMT NICHT ÜBEREIN, Start ABGEBROCHEN, erwartet: " + expectedCharset + ", gefunden: " + Charset.defaultCharset());
      } else {
        LOG.error(errorMsgPrefix + ", Start nicht abgebrochen, aktivierbar mit Property " + createProperty(DEFAULT_CHARSET_MUST_MATCH, true));
      }
    }
  }

  private void checkDefaultTimeZone() {
    LOG.info("TimeZone gefunden: " + TimeZone.getDefault());
    TimeZone expectedServerTimeZone = startupSystemDefaults.getExpectedServerTimeZone();

    if (Objects.equals(StartupSystemDefaults.KEINE_DEFAULT_TIME_ZONE, expectedServerTimeZone)) {
      LOG.info("TimeZone erwartet NICHT gefunden");
      return;
    }

    LOG.info("TimeZone erwartet: " + expectedServerTimeZone);

    if (TimeZone.getDefault().equals(expectedServerTimeZone)) {
      LOG.info("TimeZone stimmt überein");
    } else {
      String errorMsgPrefix = "TimeZone STIMMT NICHT ÜBEREIN";

      if ("true".equalsIgnoreCase(System.getProperty(DEFAULT_TIMEZONE_MUST_MATCH, "false"))) {
        LOG.error(errorMsgPrefix + ", Start ABGEBROCHEN, deaktivierbar mit Property " + createProperty(DEFAULT_TIMEZONE_MUST_MATCH, false));
        throw new RuntimeException("TimeZone STIMMT NICHT ÜBEREIN, Start ABGEBROCHEN, erwartet: " + expectedServerTimeZone + ", gefunden: " + TimeZone.getDefault());
      } else {
        LOG.info(errorMsgPrefix + ", Start nicht abgebrochen, aktivierbar mit Property " + createProperty(DEFAULT_TIMEZONE_MUST_MATCH, true));
      }
      if ("true".equalsIgnoreCase(System.getProperty(DEFAULT_TIMEZONE_SET_EXPECTED, "true"))) {
        LOG.error(errorMsgPrefix + ", ändere auf erwartete TimeZone, deaktivierbar mit Property " + createProperty(DEFAULT_TIMEZONE_SET_EXPECTED, false));

        TimeZone.setDefault(expectedServerTimeZone);
        LOG.info("TimeZone geändert: " + TimeZone.getDefault());
      } else {
        LOG.error(errorMsgPrefix + ", ändere NICHT auf erwartete TimeZone, aktivierbar mit Property " + createProperty(DEFAULT_TIMEZONE_SET_EXPECTED, true));
      }
    }
  }

  private void checkDefaultLocale() {
    LOG.info("Locale gefunden: " + Locale.getDefault());
    Locale expectedServerLocale = startupSystemDefaults.getExpectedServerLocale();

    if (Objects.equals(StartupSystemDefaults.KEIN_DEFAULT_LOCALE, expectedServerLocale)) {
      LOG.info("Locale erwartet NICHT gefunden");
      return;
    }

    LOG.info("Locale erwartet: " + expectedServerLocale);
    if (Locale.getDefault().equals(expectedServerLocale)) {
      LOG.info("Locale stimmt überein");
    } else {
      String errorMsgPrefix = "Locale STIMMT NICHT ÜBEREIN";

      if ("true".equalsIgnoreCase(System.getProperty(DEFAULT_LOCALE_MUST_MATCH, "false"))) {
        LOG.error(errorMsgPrefix + ", Start ABGEBROCHEN, deaktivierbar mit Property " + createProperty(DEFAULT_LOCALE_MUST_MATCH, false));
        throw new RuntimeException("Locale STIMMT NICHT ÜBEREIN, Start ABGEBROCHEN, erwartet: " + expectedServerLocale + ", gefunden: " + Locale.getDefault());
      } else {
        LOG.info(errorMsgPrefix + ", Start nicht abgebrochen, aktivierbar mit Property " + createProperty(DEFAULT_LOCALE_MUST_MATCH, true));
      }
      if ("true".equalsIgnoreCase(System.getProperty(DEFAULT_LOCALE_SET_EXPECTED, "false"))) {
        LOG.error(errorMsgPrefix + ", ändere auf erwartetes Locale, deaktivierbar mit Property " + createProperty(DEFAULT_LOCALE_SET_EXPECTED, false));

        Locale.setDefault(expectedServerLocale);
        LOG.info("Locale geändert: " + Locale.getDefault());
      } else {
        LOG.error(errorMsgPrefix + ", ändere NICHT auf erwartetes Locale, aktivierbar mit Property " + createProperty(DEFAULT_LOCALE_SET_EXPECTED, true));
      }
    }
  }

  private String createProperty(String property, Object b) {
    return "-D" + property + "=" + b;
  }

  private void initProtokollierung() {
    protokollAktiv.init(); // Protokollierung starten?
    LOG.info("Protokollierung von Änderungen an Entitäten ist aktiv: " + ProtokollAktiv.isProtokollierungAktiv());
    LOG.info("Protokollierung von Änderungen an Entitäten werden asynchron geschrieben: " + ProtokollAktiv.isIsProtokollierungAsynchron());
  }

  private void initApacheSystemDefaults() {
    String elParserCoerceToZero = "org.apache.el.parser.COERCE_TO_ZERO";

    /*
     * If true, when coercing EL expressions to numbers "" and null will be coerced to zero as required by the specification. If not specified, the default value of true will be used.
     *
     * Cf. http://java.net/jira/browse/JSP_SPEC_PUBLIC-184
     *
     * NOTE, dass dieses Property nur Apache(-Tomcat)-spezifisch ist und SYSTEMWEIT ÜBERSCHRIEBEN wird!!!
     */
    System.getProperties().put(elParserCoerceToZero, "false");

    StringBuilder dump = new StringBuilder();

    dump.append("Overriding org.apache.* properties");
    dump.append(LINE_SEPARATOR);
    dump.append(LINE_SEPARATOR);
    dump.append("system-parameters:");

    dump.append(LINE_SEPARATOR);
    dump.append("\tname:\t").append(elParserCoerceToZero).append(LINE_SEPARATOR);
    dump.append("\tvalue:\t").append(System.getProperty(elParserCoerceToZero));

    dump.append(LINE_SEPARATOR);

    LOG.debug(dump.toString());
  }
}
