/*
 ************************************************************************
 *
 * Copyright (c) 2003-2004, C&C Research Laboratories, NEC Europe Ltd.
 *
 * Copyright in this software belongs to C&C Research Laboratories,
 * Rathausallee 10, 53757 Sankt Augustin, Germany.
 *
 * This software may not be used, sold, licensed, transferred, copied
 * or reproduced in whole or in part in any manner or form or in or
 * on any media by any person other than in accordance with the terms
 * of the Licence Agreement supplied with the software, or otherwise
 * without the prior written consent of the copyright owners.
 *
 * This software is distributed WITHOUT ANY WARRANTY, without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE, except where stated in the Licence Agreement supplied with
 * the software.
 *
 *  Created By :           G.A. Kohring
 *  Created for Project :  GEMSS (IST-2001-37153)
 *
 ************************************************************************
 */

package de.nece.ccrle.plugins.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;
import eu.gemss.components.util.ZipArchive;


/**
 * Implements the <code>ZipArchive</code> utility for reading from zip
 * archives.
 *
 * @author Greg Kohring
 */
public class ZipArchiveImp extends ZipFile implements ZipArchive {

    /**
     * Opens the archive specified by the given filename for reading.
     *
     * @throws IOException if an I/O error occors.
     * @throws ZipException if a ZIP format error occors.
     */
    public ZipArchiveImp( String archiveFilename ) throws IOException, ZipException {
        super( archiveFilename );
    }

    /**
     * Opens the specified archive file for reading.
     *
     * @throws IOException if an I/O error occors.
     * @throws ZipException if a ZIP format error occors.
     */
    public ZipArchiveImp( File archiveFile ) throws IOException, ZipException {
        super( archiveFile );
    }

    private String[] entryNames = null;

    /**
     * Lists the names of all the entries in the archive.
     *
     * @return an array of <code>String</code>s containing the names of all
     *      the entries in the archive. An empty archive has an array of
     *      zero length.
     */
    public String[] getEntryNames(){

        if ( entryNames == null ){

            entryNames = new String[ size() ];

            int i = 0;
            for ( Enumeration ee = entries(); ee.hasMoreElements(); i++) {

                ZipEntry entry = (ZipEntry) ee.nextElement();

                entryNames[i] = entry.getName();
            }
        }

        return entryNames;
    }

    /**
     * Extracts the specified entry from the archive and places it into 
     * the specified directory.
     *
     * @param entryName a <code>String</code> containing the name of the entry
     *      which should be extracted.
     * @param destination a <code>File</code> object naming a directory into
     *      which the archive should be unpacked.
     * @throws FileNotFoundException if 
     *      the specified destination is not a directory.
     * @throws IOException if an I/O error occurs.
     * @throws ZipException if the specified entry does not exist in this
     *      archive or if any other Zip file error occurs.
     */
    public void extract( String entryName, File destination )
            throws FileNotFoundException, IOException, ZipException {

        ZipEntry entry = getEntry( entryName );

        if ( entry == null ){
            throw new ZipException( " Entry not found: " + entryName );
        }
            
        extractEntry( entry, destination );
    }

    /**
     * Extracts the entire contents of the archive into the specified directory.
     *
     * @param destination a <code>File</code> object naming a directory into
     *      which the archive should be unpacked.
     * @throws FileNotFoundException if 
     *      the specified destination is not a directory.
     * @throws IOException if an I/O error occurs.
     * @throws ZipException if any Zip file error occurs.
     */
    public void extractAll(  File destination ) 
            throws FileNotFoundException, IOException, ZipException {

        if ( !destination.isDirectory() ){
            throw new FileNotFoundException( destination.toString() 
                                + " is not a directory! " );
        }

        for ( Enumeration ee = entries(); ee.hasMoreElements(); ) {

            ZipEntry entry = (ZipEntry) ee.nextElement();

            extractEntry( entry, destination );
        }

    }

    // Extracts the specified entry to the specified destination
    private void extractEntry( ZipEntry entry, File destination )
            throws FileNotFoundException, IOException, ZipException {

        final int BUFFER = 4096;
        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        int count;
        byte data[] = new byte[BUFFER];

        File outFile = new File( destination, entry.getName() );

        if ( entry.isDirectory() ){
            if ( !outFile.exists() ) {
                if ( !outFile.mkdir() ){
                    throw new IOException( "unable to make directory: " +
                                                outFile.toString() );
                }
            } else {
                if ( !outFile.isDirectory() ){
                    throw new FileNotFoundException( 
                                "directory exists: " + outFile.toString() );
                }
            }
        } else {

            bis = new BufferedInputStream( getInputStream( entry ) );
            bos = new BufferedOutputStream(
                        new FileOutputStream( outFile ), BUFFER );

            while ( ( count = bis.read( data, 0, BUFFER ) ) != -1 ) {
                bos.write( data, 0, count );
            }

            bos.flush();
            bos.close();
            bis.close();

        }

        if ( entry.getTime() != -1 ){
            outFile.setLastModified( entry.getTime() );
        }

    }


}
