package de.ivu.elect.nl.eml;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

import javax.annotation.Nullable;

import org.w3._2000._09.xmldsig_.CanonicalizationMethodType;

import de.ivu.elect.jee.exchange.JAXBUtil;
import de.ivu.wahl.wus.oasis.eml.EML;
import de.ivu.wahl.wus.oasis.eml.kiesraad.CreatedByAuthority;
import de.ivu.wahl.wus.oasis.eml.kiesraad.ListData;


import static de.ivu.elect.nl.eml.XmlHelper.conditionalCanonicalizeXml;
import static de.ivu.elect.nl.eml.XmlHelper.createHash;
import static de.ivu.elect.nl.eml.XmlHelper.createMarshallerProperties;

public class EmlWriter {

  private static final boolean CANONICALIZE_ELECTVAPP578 = true; // ELECTVAPP-578

  public void writeEMlNoHash(EmlWrapper emlWrapper, OutputStream os) {
    writeEMl(emlWrapper, os, null, emlWrapper.getEmlConfig().isUseCanonicalize(), emlWrapper.getEmlConfig().isPrettyPrint());
  }

  public void writeEMlHash(EmlWrapper emlWrapper, OutputStream os, Consumer<String> hashConsumer) {
    writeEMl(emlWrapper, os, hashConsumer, emlWrapper.getEmlConfig().isUseCanonicalize(), emlWrapper.getEmlConfig().isPrettyPrint());
  }

  protected void writeEMl(EmlWrapper emlWrapper, OutputStream os, @Nullable Consumer<String> hashConsumer, boolean canonicalize, boolean prettyPrint) {
    writeEMl(emlWrapper, os, hashConsumer, canonicalize, canonicalize, prettyPrint);
  }

  protected void writeEMl(EmlWrapper emlWrapper, OutputStream os, @Nullable Consumer<String> hashConsumer, boolean canonicalize,
                          boolean includeCanonicalizeNamespace, boolean prettyPrint) {
    try {
      JAXBUtil jaxbUtil = new JAXBUtil();
      try (ByteArrayOutputStream finalBos = new ByteArrayOutputStream(EmlReader.BUFFER_SIZE)) {
        try (ByteArrayOutputStream jaxbBos = new ByteArrayOutputStream(EmlReader.BUFFER_SIZE)) {

          List<Class<?>> classes = new ArrayList<>(Arrays.asList(EML.class, ListData.class, CreatedByAuthority.class));
          if (includeCanonicalizeNamespace) {
            classes.add(CanonicalizationMethodType.class); // ELECTVAPP-984
          }
          jaxbUtil.marshal(emlWrapper.getContent(), jaxbBos, createMarshallerProperties(prettyPrint), classes.toArray(new Class[0]));
          if (canonicalize) {
            try (ByteArrayInputStream bis = new ByteArrayInputStream(jaxbBos.toByteArray())) {
              conditionalCanonicalizeXml(bis, finalBos);
            }
          } else {
            jaxbBos.writeTo(finalBos);
          }

          if (hashConsumer != null) {
            try (ByteArrayInputStream bis = new ByteArrayInputStream(finalBos.toByteArray())) {
              hashConsumer.accept(createHash(bis));
            }
          }
          finalBos.writeTo(os);
        }
      }
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }
}
