/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.utils;

import com.queplix.core.utils.log.AbstractLogger;
import com.queplix.core.utils.log.Log;

import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * Helper class to work with files.
 *
 * @author [ALB] Andrey Baranov
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:31:15 $
 */

public final class FileHelper {

    // ----------------------------------------------------- constants

    // Logger instance.
    private static final AbstractLogger logger = Log.getLog(FileHelper.class);

    // ----------------------------------------------------- constructor

    // Private constructor - blocks instantiation.

    private FileHelper() {
    }

    // ----------------------------------------------------- public methods

    /**
     * Create directory.
     *
     * @param dir directory
     * @throws IOException
     */
    public static void createDirectory(File dir)
            throws IOException {

        if(dir == null) {
            throw new NullPointerException("Directory is NULL");
        }

        if(!dir.exists()) {
            if(!dir.mkdirs()) {
                throw new FileNotFoundException(
                        "Can't create SA directory '" + dir + "'.");
            }
        } else if(!dir.isDirectory()) {
            throw new IOException("Not a directory: '" + dir + "'.");
        }
    }

    /**
     * Deletes the existing file or directory.
     *
     * @param file File object
     * @return <b>true</b> on success, <b>false</b> on fail
     */
    public static boolean delete(File file) {

        if(file == null) {
            throw new NullPointerException("File is NULL");
        }

        if(file.isDirectory()) {
            File[] files = file.listFiles();
            for(int i = 0; i < files.length; i++) {
                delete(files[i]);
            }
        }

        return file.delete();
    }

    /**
     * Loads the file content.
     *
     * @param file File objetc
     * @return array of bytes
     * @throws IOException
     */
    public static byte[] loadFile(File file)
            throws IOException {

        if(file == null) {
            throw new NullPointerException("File is NULL");
        }

        FileInputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = new FileInputStream(file);
            out = new ByteArrayOutputStream();

            int len;
            byte[] buf = new byte[1024];
            while((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }

            return out.toByteArray();

        } finally {
            try {
                if(in != null) {
                    in.close();
                }
            } catch (Exception ex) {
            }
            try {
                if(out != null) {
                    out.close();
                }
            } catch (Exception ex) {
            }
        }

    }

    /**
     * Loads the file content.
     *
     * @param file File objetc
     * @param enc  file encoding
     * @return data as a array of chars
     * @throws IOException
     */
    public static final char[] loadFile(File file, String enc)
            throws IOException {

        char[] ret;
        CharArrayWriter caw = new CharArrayWriter(1024 * 4);
        InputStreamReader isr = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            if(enc == null) {
                isr = new InputStreamReader(fis);
            } else {
                isr = new InputStreamReader(fis, enc);
            }

            int i = 0;
            char[] data = new char[1024];
            while((i = isr.read(data)) > 0) {
                caw.write(data, 0, i);
            }
            ret = caw.toCharArray();

        } finally {
            try {
                if(isr != null) {
                    isr.close();
                }
            } catch (Exception ex) {
            }
            try {
                caw.close();
            } catch (Exception ex) {
            }
        }

        return ret;
    }

    /**
     * Writes binary data to disk.
     *
     * @param file File object
     * @param data file data
     * @throws IOException
     */
    public static void writeFile(File file, byte[] data)
            throws IOException {

        if(file == null) {
            throw new NullPointerException("File is NULL");
        }

        FileOutputStream fout = null;
        try {
            fout = new FileOutputStream(file);
            fout.write(data);
            fout.flush();

        } finally {
            if(fout != null) {
                try {
                    fout.close();
                } catch (Exception ex) {
                }
            }
        }
    }

    /**
     * Writes string data to disk.
     *
     * @param file File object
     * @param data file data
     * @param enc  file encoding
     * @throws IOException
     */
    public static final void writeFile(File file, char[] data, String enc)
            throws IOException {

        if(file == null) {
            throw new NullPointerException("File is NULL");
        }

        FileOutputStream fout = null;
        try {
            fout = new FileOutputStream(file);
            OutputStreamWriter osw;
            if(enc == null) {
                osw = new OutputStreamWriter(fout);
            } else {
                osw = new OutputStreamWriter(fout, enc);
            }
            osw.write(data);
            osw.flush();
            osw.close();

        } finally {
            if(fout != null) {
                try {
                    fout.close();
                } catch (Exception ex) {
                }
            }
        }
    }

    /**
     * <p>Copy file <code>src</code> to the file <code>dest</code>.</p>
     *
     * @param src  source file
     * @param dest destination file
     * @throws IOException
     */
    public static void copyFile(File src, File dest)
            throws IOException {
        copyFile(src, dest, null, null);
    }

    /**
     * <p>Copy file <code>src</code> to the file <code>dest</code>.</p>
     *
     * @param src     source file
     * @param dest    destination file
     * @param srcEnc  source files encoding
     * @param destEnc destination files encoding
     * @throws IOException
     */
    public static void copyFile(File src,
                                File dest,
                                String srcEnc,
                                String destEnc)
            throws IOException {

        if(src == null) {
            throw new NullPointerException("Src is NULL");
        }
        if(dest == null) {
            throw new NullPointerException("Dest is NULL");
        }

        if(srcEnc == null && destEnc == null) {
            // Load and save file without encodings
            byte[] data = loadFile(src);
            writeFile(dest, data);

        } else {
            // Load and save file with encodings
            char[] data = loadFile(src, srcEnc);
            writeFile(dest, data, destEnc);
        }
    }

    /**
     * Deletes the existing file.
     *
     * @param file File object
     * @throws IOException
     */
    public static void deleteFile(File file)
            throws IOException {

        if(file.exists()) {
            if(!file.delete()) {
                throw new IOException("Can't delete file '" + file
                        + "' - internal I/O error.");
            }
        } else {
            logger.WARN("File '" + file + "' doesn't exist.");
        }
    }

    /**
     * Gets all subdirectories of the given directory, unfolded into the flat list.
     *
     * @param dir given directory
     * @return List of subdirectories
     */
    public static List getDirectoriesList(File dir) {

        List list = new ArrayList();
        File[] subdirs = dir.listFiles(new FileFilter() {
            public boolean accept(File file) {
                return file.isDirectory();
            }
        });

        if(subdirs != null) {
            for(int i = 0; i < subdirs.length; i++) {
                list.add(subdirs[i]);
                list.addAll(getDirectoriesList(subdirs[i]));
            }
        }

        return list;

    }

    /**
     * Gets list of files in the given directory and it's subdirectories
     *
     * @param dir given directory
     * @return List of files
     */
    public static List getFilesList(File dir) {

        List list = new ArrayList();

        List dirs = getDirectoriesList(dir);
        dirs.add(dir);

        for(int i = 0; i < dirs.size(); i++) {
            File currentDir = (File) (dirs.get(i));
            File[] files = currentDir.listFiles(new FileFilter() {
                public boolean accept(File file) {
                    return (file.isFile());
                }
            });

            if(files != null) {
                for(int j = 0; j < files.length; j++) {
                    list.add(files[j]);
                }
            }
        }

        return list;
    }

    /**
     * Calculates directory size.
     * @param dirPath directory path
     * @result total size in bytes of all files in the directory and subdirectories
     */
//    public static long calculateDirSize(File dir) {
//        long result = 0;
//        if (dir.isDirectory()) {
//            File[] files = dir.listFiles();
//            for (int i = 0; i < files.length; i++) {
//                File file = files[i];
//                if (file.isFile()) {
//                    result += file.length();
//                } else if (file.isDirectory()) {
//                    result += calculateDirSize(file.getPath()); // recursive call
//                }
//            }
//        } else { // incorrect path or not a directory
//            result = -1;
//        }
//        return result;
//    }

    /**
     * Calculates directory size.
     * @param dir directory path
     * @result total size in bytes of all files in the directory and subdirectories
     *
     * @see FileHelper#calculateDirSize(File)
     */
//    public static long calculateDirSize(String dirPath) {
//        return calculateDirSize(new File(dirPath));
//    }

    /**
     * Calculates directory or file size.
     *
     * @param fileOrDir File instance that represents any file or directory
     * @result total size in bytes of all files in the directory and subdirectories
     */
    public static long calculateFileSize(File fileOrDir) {
        if(fileOrDir.isFile()) {
            return fileOrDir.length();
        }

        long result = 0;
        if(fileOrDir.isDirectory()) {
            File[] files = fileOrDir.listFiles();
            for(int i = 0; i < files.length; i++) {
                result += calculateFileSize(files[i]);
            }
        }
        return result;
    }

    /**
     * Calculates directory or file size.
     *
     * @param fileOrDir Path that represents any file or directory
     * @result total size in bytes of all files in the directory and subdirectories
     */
    public static long calculateFileSize(String fileOrDir) {
        return calculateFileSize(new File(fileOrDir));
    }
}
