// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.opendata.core.io.archive;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLStreamException;

import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.io.IllegalDataException;
import org.openstreetmap.josm.plugins.opendata.core.datasets.AbstractDataSetHandler;
import org.openstreetmap.josm.tools.Logging;

public class ZipReader extends ArchiveReader {

    private final ZipInputStream zis;

    public ZipReader(InputStream in, AbstractDataSetHandler handler, boolean promptUser) {
        super(handler, handler != null ? handler.getArchiveHandler() : null, promptUser);
        this.zis = in instanceof ZipInputStream ? (ZipInputStream) in : new ZipInputStream(in);
    }

    public static DataSet parseDataSet(InputStream in, AbstractDataSetHandler handler, ProgressMonitor instance, boolean promptUser)
            throws IOException, XMLStreamException, FactoryConfigurationError, IllegalDataException {
        return new ZipReader(in, handler, promptUser).parseDoc(instance);
    }

    public static Map<File, DataSet> parseDataSets(InputStream in, AbstractDataSetHandler handler, ProgressMonitor instance, boolean promptUser)
            throws IOException, XMLStreamException, FactoryConfigurationError, IllegalDataException {
        return new ZipReader(in, handler, promptUser).parseDocs(instance);
    }

    @Override
    protected void extractArchive(final File temp, final List<File> candidates) throws IOException, FileNotFoundException {
        ZipEntry entry;
        while ((entry = zis.getNextEntry()) != null) {
            Logging.debug("Extracting {0}", entry.getName());
            File file = new File(temp + File.separator + entry.getName());
            File parent = file.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            if (file.exists() && !file.delete()) {
                throw new IOException("Could not delete temp file/dir: " + file.getAbsolutePath());
            }
            if (!entry.isDirectory()) {
                if (!file.createNewFile()) {
                    throw new IOException("Could not create temp file: " + file.getAbsolutePath());
                }
                // Write temp file
                try (FileOutputStream fos = new FileOutputStream(file)) {
                    byte[] buffer = new byte[8192];
                    int count;
                    while ((count = zis.read(buffer, 0, buffer.length)) > 0) {
                        fos.write(buffer, 0, count);
                    }
                }
                // Allow handler to perform specific treatments (for example, fix invalid .prj files)
                if (archiveHandler != null) {
                    archiveHandler.notifyTempFileWritten(file);
                }
                // Set last modification date
                long time = entry.getTime();
                if (time > -1) {
                    file.setLastModified(time);
                }
                lookForCandidate(entry.getName(), candidates, file);
            } else if (!file.mkdir()) {
                throw new IOException("Could not create temp dir: " + file.getAbsolutePath());
            }
        }
    }

    @Override
    protected String getTaskMessage() {
        return tr("Reading Zip file...");
    }
}
