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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.inject.Inject;

import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.datei.entity.FileTypeResolver;
import de.ivu.elect.business.exchange.control.ImportExportStreamCreator;
import de.ivu.elect.business.konfiguration.control.StreamCreator;

public class DateiLeser extends AbstractDateiLeser {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient FileTypeResolver fileTypeResolver;

  @Inject
  private transient ImportExportStreamCreator streamCreator;

  public DateiLeser() {
    super();
  }

  public List<String> liesDatenAusDatei(StreamCreator streamCreator, Datei datei, Charset charset) throws IOException {
    return liesDatenAusDatei(streamCreator, datei, charset, Integer.MAX_VALUE);
  }

  public List<String> liesDatenAusDatei(Datei datei, Charset charset, int maxCount) throws IOException {
    return liesDatenAusDatei(streamCreator, datei, charset, maxCount);
  }

  private List<String> liesDatenAusDatei(StreamCreator streamCreator, Datei datei, Charset charset, int maxCount) throws IOException {
    if (datei.isZipDatei()) {
      return readZipFile(streamCreator, datei, charset, maxCount);
    }

    if (datei.isOdtDatei()) {
      return readZipEntryNames(streamCreator, datei, charset, maxCount);
    }

    return readLines(streamCreator, datei, charset, maxCount);
  }

  private List<String> readZipFile(StreamCreator streamCreator, Datei datei, Charset charset, int maxCount) throws IOException {
    if (containsSinglePlainTextZipEntry(streamCreator, datei)) {
      try {
        return readSinglePlainTextZipEntry(streamCreator, datei, charset, maxCount);
      } catch (Exception ignored) {
        // Try read ZIP-entry names...
      }
    }

    return readZipEntryNames(streamCreator, datei, charset, maxCount);
  }

  private boolean containsSinglePlainTextZipEntry(StreamCreator streamCreator, Datei datei) {
    try (ZipInputStream zis = createZipInputStream(streamCreator, datei)) {
      StringBuilder zipEntryName = new StringBuilder();
      ZipEntry zipEntry;

      while ((zipEntry = zis.getNextEntry()) != null) {
        if (zipEntryName.length() == 0) {
          zipEntryName.append(zipEntry.getName());
        } else {
          zipEntryName.setLength(0);

          break;
        }
      }

      return zipEntryName.length() > 0 && fileTypeResolver.getFileType(zipEntryName)
          .map(fileType -> fileType.isVorschauMoeglich() && !fileType.isArchive()).orElse(false);
    } catch (Exception ignored) {
    }

    return false;
  }

  private List<String> readSinglePlainTextZipEntry(StreamCreator streamCreator, Datei datei, Charset charset, int maxLinesCount)
      throws IOException {
    try (ZipInputStream zis = createZipInputStream(streamCreator, datei)) {
      zis.getNextEntry();

      try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(zis, charset))) {
        return readLines(bufferedReader, maxLinesCount);
      }
    }
  }

  private List<String> readZipEntryNames(StreamCreator streamCreator, Datei datei, Charset charset, int maxZipEntryNamesCount)
      throws IOException {
    List<String> result = new ArrayList<>();

    try (ZipInputStream zis = createZipInputStream(streamCreator, datei)) {
      for (int i = 0; i < maxZipEntryNamesCount; i++) {
        ZipEntry zipEntry = zis.getNextEntry();

        if (zipEntry == null) {
          break;
        }

        result.add(zipEntry.getName());
      }
    }

    return result;
  }

  private List<String> readLines(StreamCreator streamCreator, Datei datei, Charset charset, int maxBytesCount) throws IOException {
    int maxLinesCount = Integer.MAX_VALUE;

    return readLines(streamCreator, datei, charset, maxBytesCount, maxLinesCount);
  }
}
