/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2005 Bull S.A.
 * Contact: jonas-team@objectweb.org
 *
 * This 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 any later version.
 *
 * This 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.objectweb.jonas_lib.files;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.objectweb.jonas.common.Log;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * This class manages operation done many times by JOnAS on files, like copying
 * them.
 * @author Florent Benoit
 */
public class FileUtils {


    /**
     * Size of the buffer.
     */
    private static final int BUFFER_SIZE = 2048;


    /**
     * Logger
     */
    private static Logger logger = Log.getLogger(FileUtils.class.getName());

    /**
     * Utility class, no public constructor
     */
    private FileUtils() {

    }

    /**
     * Unpack the source archive in a given directory and returns directory
     * directory created.
     * @param packedJar source JarFile to be unpacked
     * @param dest the destination folder
     * @throws FileUtilsException When unpack fails
     */
    public static void unpack(JarFile packedJar, File dest) throws FileUtilsException {

        JarEntry entry = null;

        // get entries of the jar file
        Enumeration entries = packedJar.entries();
        while (entries.hasMoreElements()) {
            entry = (JarEntry) entries.nextElement();

            // File entry
            File entryFile = new File(dest, entry.getName());

            // Create directory
            if (entry.isDirectory()) {
                if (!entryFile.exists()) {
                    // create parent directories (with mkdirs)
                    if (!entryFile.mkdirs()) {
                        String err = "Can not create directory " + entryFile + ", Check the write access.";
                        throw new FileUtilsException(err);
                    }
                }
                continue;
            }

            // If it's a file, we must extract the file
            // Ensure that the directory exists.
            entryFile.getParentFile().mkdirs();

            InputStream is = null;
            // get the input stream
            try {
                is = packedJar.getInputStream(entry);
                // Dump to the file
                dump(is, entryFile);
            } catch (IOException ioe) {
                throw new FileUtilsException("Cannot get inputstream of entry '" + entry + "' of file '" + packedJar
                        + "'.");
            } finally {
                try {
                    is.close();
                } catch (IOException ioe) {
                    if (logger.isLoggable(BasicLevel.DEBUG)) {
                        logger.log(BasicLevel.DEBUG, "Cannot close input stream", ioe);
                    }
                }
            }

        }
    }

    /**
     * Write the given input stream in the given file.
     * @param in the inputStream to copy.
     * @param entryFile the file where the inputStream must be dumped.
     * @throws FileUtilsException if the dump failed.
     */
    private static void dump(InputStream in, File entryFile) throws FileUtilsException {

        try {
            //File output
            FileOutputStream out = new FileOutputStream(entryFile);
            int n = 0;
            try {
                //buffer
                byte[] buffer = new byte[BUFFER_SIZE];
                n = in.read(buffer);
                while (n > 0) {
                    out.write(buffer, 0, n);
                    n = in.read(buffer);
                }
            } finally {
                out.close();
            }
        } catch (IOException e) {
            String err = "Error while unpacking entry " + entryFile + " : ";
            throw new FileUtilsException(err, e);
        }
    }

    /**
     * Copy a file
     * @param src source file
     * @param dest dest file
     * @throws FileUtilsException if the copy of the file failed
     */
    public static void copyFile(String src, String dest) throws FileUtilsException {
        copyFile(new File(src), new File(dest));
    }

    /**
     * Copy a file
     * @param src source file
     * @param dest dest file
     * @throws FileUtilsException if the copy of the file failed
     */
    public static void copyFile(File src, File dest) throws FileUtilsException {

        if (dest.isDirectory()) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Copy a file to a directory, append source filename to directory.");
            }
            dest = new File(dest, src.getName());
        }

        FileInputStream fIn = null;
        FileOutputStream fOut = null;
        FileChannel fcIn = null;
        FileChannel fcOut = null;
        try {

            // InputStream
            fIn = new FileInputStream(src);
            fOut = new FileOutputStream(dest);

            // nio channel
            FileChannel sourceFC = fIn.getChannel();
            FileChannel targetFC = fOut.getChannel();

            targetFC.transferFrom(sourceFC, 0, sourceFC.size());
        } catch (Exception e) {
            throw new FileUtilsException("Error during copy file : " + src + " -> " + dest, e);
        } finally {
            try {
                fOut.close();
                fIn.close();
                fcOut.close();
                fcIn.close();
            } catch (Exception e) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Cannot close some i/o which are open.", e);
                }
            }

        }
    }

    /**
     * @param path file/directory to be deleted
     * @return true if deletion was OK
     */
    public static boolean delete(String path) {
        return delete(new File(path));
    }

    /**
     * @param f file/directory to be deleted
     * @return true if deletion was OK
     */
    public static boolean delete(File f) {
        if (f.isFile()) {
            return f.delete();
        } else {
            File[] childs = f.listFiles();
            if (childs == null) {
                // no childs
                return f.delete();
            } else {
                // childs
                boolean result = true;
                for (int i = 0; i < childs.length; i++) {
                    result &= delete(childs[i]);
                }
                return result && f.delete();
            }
        }

    }

    /**
     * Copy a directory recursively
     * @param src source directory
     * @param dest dest directory
     * @throws FileUtilsException if the copy of the directory failed
     */
    public static void copyDirectory(String src, String dest) throws FileUtilsException {
        copyDirectory(new File(src), new File(dest));
    }

    /**
     * Copy a directory recursively
     * @param src source directory
     * @param dest dest directory
     * @throws FileUtilsException if the copy of the directory failed
     */
    public static void copyDirectory(File src, File dest) throws FileUtilsException {

        if (!src.isDirectory()) {
            // We don not accept file arguments !
            throw new IllegalArgumentException("Source '" + src + "' must be a directory");
        }

        // create the destination directory if it is inexistant
        if (!dest.exists()) {
            dest.mkdirs();
        }

        // copy the files of the source directory
        File[] childs = src.listFiles();
        if (childs != null) {
            // childs
            for (int i = 0; i < childs.length; i++) {
                File child = childs[i];
                if (child.isFile()) {
                    // file
                    copyFile(child, dest);
                } else {
                    // directory
                    copyDirectory(child, new File(dest, child.getName()));
                }
            }
        }
    }
    
    /**
     * return a list of files with this suffix in directory dstr
     */
    public static String [] getFileList(File dir, String prefix, String suffix) {
        return dir.list(new DirFilter(prefix, suffix));
    }

}
