/**
 * Copyright (c) 2005-2012 EBM WebSourcing, 2007-2009 Capgemini Sud, 2012-2022 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.plugin.jbiplugin.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.IOUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;

/**
 * @author Christophe Deneux - Capgemini Sud
 */
public class ZipUtil {
    
    // It's a tooling class
    private ZipUtil() {
        // NOP
    }

    /**
     * Add a file to a zip stream, without checking if already zipped.
     * 
     * @param zipOutputStream OutputStream of the zip file
     * @param inputStream InputStream of the file to add
     * @param zipEntryName Zip entry name of the file to add
     */
    public final static void addFile(final ZipOutputStream zipOutputStream, final InputStream inputStream,
            final String zipEntryName) throws IOException {
        
        ZipEntry zipEntry = new ZipEntry(zipEntryName);
        
        ZipUtil.putFileEntry(zipOutputStream, inputStream, zipEntry);
    }
    
    /**
     * Add a file to a zip stream specifying zip entry time, without checking if already zipped.
     * 
     * @param zipOutputStream OutputStream of the zip file
     * @param inputStream InputStream of the file to add
     * @param zipEntryName Zip entry name of the file to add
     * @param zipEntryTime Zip entry time of the file to add
     */
    public final static void addFile(final ZipOutputStream zipOutputStream, final InputStream inputStream,
            final String zipEntryName, final long zipEntryTime) throws IOException {
        
        ZipEntry zipEntry = new ZipEntry(zipEntryName);
        zipEntry.setTime(zipEntryTime);
        
        ZipUtil.putFileEntry(zipOutputStream, inputStream, zipEntry);
    }
    
    /**
     * Add a zip entry file to a zip stream, without checking if already zipped.
     * 
     * @param zipOutputStream OutputStream of the zip file
     * @param inputStream InputStream of the file to add
     * @param zipEntry Zip entry of the file to add
     */
    private final static void putFileEntry(final ZipOutputStream zipOutputStream, final InputStream inputStream,
            final ZipEntry zipEntry) throws IOException {

            zipOutputStream.putNextEntry(zipEntry);
        try {
            IOUtils.copy(inputStream, zipOutputStream);
        } finally {
            zipOutputStream.closeEntry();
        }
    }
    
    public interface ZipEntryCallback {
        public InputStream onZipEntry(final ZipEntry zipEntry, final InputStream zipEntryInputStream) throws IOException, MojoExecutionException;

        /**
         * 
         * @return List of extra artifacts to add to the archive. <code>null</code> if nothing to add.
         */
        public List<Artifact> getExtraArtifactsToAdd();
        
        /**
         * 
         * @return List of extra artifacts to add to the archive. <code>null</code> if nothing to add.
         */
        public List<ByteArrayOutputStreamEntry> getExtraFilesToAdd();
    }
    
    public final static void copyAndUpdateZipFile(final ZipInputStream zipInputFile,
            final ZipOutputStream zipOutputStream, final ZipEntryCallback callback, final Log log)
            throws IOException, MojoExecutionException {

    	// Process the original archive content
    	{
	        ZipEntry zipEntry;
	    	while ((zipEntry = zipInputFile.getNextEntry()) != null) {

            final ZipEntry newZipEntry = new ZipEntry(zipEntry.getName());
            newZipEntry.setComment(zipEntry.getComment());
            newZipEntry.setTime(zipEntry.getTime());
            ZipUtil.putFileEntry(zipOutputStream, callback.onZipEntry(newZipEntry, zipInputFile
	                   ), newZipEntry);

	        }
    	}

        // Perhaps some files (as artifact) should be added
    	{
	    	final List<Artifact> extraEntriesToAdd = callback.getExtraArtifactsToAdd();
	    	if (extraEntriesToAdd != null) {
		        for (final Artifact newArtifact : extraEntriesToAdd) {
		        	
		        	final File newArtifactFile = newArtifact.getFile();
		        	
		        	// Add the artifact to the JBI archive
		            final ZipEntry newZipEntry = new ZipEntry(newArtifactFile.getName());
		            newZipEntry.setComment("");
		            newZipEntry.setTime(newArtifactFile.lastModified());
		            final InputStream isArtifact = new FileInputStream(newArtifactFile);
		            try {
		                ZipUtil.putFileEntry(zipOutputStream, isArtifact, newZipEntry);
		            } finally {
		                isArtifact.close();
		            }
		        }
	    	}
    	}

        // Perhaps some files (as bytearrayoutputstream) should be added
    	{
	    	final List<ByteArrayOutputStreamEntry> filesToAdd = callback.getExtraFilesToAdd();
	    	if (filesToAdd != null) {
		        for (final ByteArrayOutputStreamEntry baosEntry : filesToAdd) {
		        	final String entryName = baosEntry.getName();
		        	log.info("Add as extra file: " + entryName);
	                final ZipEntry newZipEntry = new ZipEntry(entryName);
		        	newZipEntry.setComment("");
		            newZipEntry.setTime(System.currentTimeMillis());
		            final InputStream isFile = new ByteArrayInputStream(baosEntry.getBaos().toByteArray());
		            try {
		            	ZipUtil.putFileEntry(zipOutputStream, isFile, newZipEntry);
		            } finally {
		                isFile.close();
		            }
		        }
	    	}
    	}
    }

    /**
     * Add a directory to a zip stream, without checking if already zipped.
     * 
     * @param zipOutputStream OutputStream of the zip file
     * @param zipEntryName Zip entry name of the directory to add
     */
    public final static void addDirectory(final ZipOutputStream zipOutputStream, final String zipEntryName) throws IOException {
        final ZipEntry zipEntry = new ZipEntry(zipEntryName+"/");
        ZipUtil.putDirectoryEntry(zipOutputStream, zipEntry);
    }

    /**
     * Add a zip directory entry to a zip stream, without checking if already zipped.
     * 
     * @param zipOutputStream OutputStream of the zip file
     * @param zipEntry Zip entry of the directory to add
     * @throws <IOException>
     */
    private static void putDirectoryEntry(final ZipOutputStream zipOutputStream, final ZipEntry zipEntry) throws IOException {
        zipOutputStream.putNextEntry(zipEntry);
        zipOutputStream.closeEntry();
    }
}
