
package de.ivu.elect.jee.exchange;

import java.io.File;
import java.net.URL;

import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.util.ValidationEventCollector;

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


import static javax.xml.bind.ValidationEvent.ERROR;
import static javax.xml.bind.ValidationEvent.FATAL_ERROR;

public class XmlViolationCollector implements ValidationEventHandler {

  private static final String URL_SEPARATOR = "/";

  private static final String XML_SCHEMA_VIOLATION_MESSAGE_KEY = "validation.xml.does.not.conform.to.schema.error";

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

  private final ValidationEventCollector validationEventCollector;

  private String schemaFileName;

  public XmlViolationCollector() {
    this.validationEventCollector = new ValidationEventCollector();
  }

  @Override
  public boolean handleEvent(ValidationEvent event) {
    return validationEventCollector.handleEvent(event);
  }

  public Violations getViolations() {
    return convertValidationResultsToViolations(validationEventCollector.getEvents());
  }

  private Violations convertValidationResultsToViolations(ValidationEvent[] events) {
    Violations violations = new Violations();

    for (ValidationEvent event : events) {
      if (isWarnungFuerEventAusgebenNachSchwereDesEvents(event)) {
        behandleEventAlsWarnung(event, violations);
      } else {
        behandleEventAlsInfo(event);
      }
    }

    return violations;
  }

  private boolean isWarnungFuerEventAusgebenNachSchwereDesEvents(ValidationEvent event) {
    return event.getSeverity() == ERROR || event.getSeverity() == FATAL_ERROR;
  }

  private void behandleEventAlsWarnung(ValidationEvent event, Violations violations) {

    // keine Ahnung, ob die Exception je NULL sein kann
    if (event.getLinkedException() == null) {
      LOG.warn("XML-Validierung gegen Schema fehlgeschlagen: " + event.getMessage());
    } else {
      // Kein Stacktrace ausgeben, müllt nur das Logfile zu...
      LOG.warn("XML-Validierung gegen Schema fehlgeschlagen." + event.getLinkedException().getMessage()+", " + event.getMessage());
    }

    String zeile = "?";
    String spalte = "?";
    if (event.getLocator() != null) {
      zeile = Integer.toString(event.getLocator().getLineNumber());
      spalte = Integer.toString(event.getLocator().getColumnNumber());
    }

    if (violations.isEmpty()) { // nicht mehrere Warnungen ausgeben,  warum? Theoretisch könnte man die "SEVERITY" auswerten
      violations.addWarning(XML_SCHEMA_VIOLATION_MESSAGE_KEY, schemaFileName, zeile, spalte, event.getMessage());
    }
  }

  private void behandleEventAlsInfo(ValidationEvent event) {
    if (event.getLinkedException() != null) {
      LOG.info("Die XML-Validierung berichtete ein Problem.", event.getLinkedException());
    }
  }

  // ELECT-1266
  public void setSchemaLocation(URL schemaLocation) {
    this.schemaFileName = convertUrlToFileNameOrNull(schemaLocation);
  }

  public void setSchemaLocation(File schemaLocation) {
    this.schemaFileName = schemaLocation.getPath();
  }

    private String convertUrlToFileNameOrNull(URL url) {
    String urlString = url.getPath();
    if (urlString.isEmpty()) {
      return url.toExternalForm();
    }

    int lastSeparator = urlString.lastIndexOf(URL_SEPARATOR);
    if (-1 < lastSeparator && lastSeparator < urlString.length()) {
      return urlString.substring(lastSeparator + 1);
    } else {
      return urlString;
    }
  }
}
