/**
 *  Files
 *  Copyright 2011 by Michael Peter Christen, mc@yacy.net, Frankfurt a. M., Germany
 *  First released 29.06.2011 at https://yacy.net
 *
 *  $LastChangedDate: 2011-05-30 10:53:58 +0200 (Mo, 30 Mai 2011) $
 *  $LastChangedRevision: 7759 $
 *  $LastChangedBy: orbiter $
 *
 *  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 (at your option) 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 program in the file lgpl21.txt
 *  If not, see <http://www.gnu.org/licenses/>.
 */

package net.yacy.cora.storage;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.zip.GZIPInputStream;

import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;

public class Files {

	/**
	 * open text files for reading. If the files are compressed, choose the
	 * appropriate decompression method automatically
	 * @param f
	 * @return the input stream for the file
	 * @throws IOException
	 */
	public static InputStream read(File f) throws IOException {

        // make input stream
        InputStream is = new BufferedInputStream(new FileInputStream(f));
        if (f.toString().endsWith(".bz2")) is = new BZip2CompressorInputStream(is);
        if (f.toString().endsWith(".gz")) is = new GZIPInputStream(is);

        return is;
	}

	/**
	 * reading a file line by line should be done with two concurrent processes
	 * - one reading the file and doing IO operations
	 * - one processing the result
	 * This method makes is easy to create concurrent file readers by providing
	 * a process that fills a blocking queue with lines from a file.
	 * After the method is called, it returns immediately a blocking queue which is
	 * filled concurrently with the lines of the file. When the reading is finished,
	 * this is signalled with a poison entry, the POISON_LINE String which can be
	 * compared with an "==" operation.
	 * @param f the file to read
	 * @param maxQueueSize
	 * @return a blocking queue which is filled with the lines, terminated by POISON_LINE
	 * @throws IOException
	 */
	public final static String POISON_LINE = "__@POISON__";
	public static BlockingQueue<String> concurentLineReader(final File f) throws IOException {
		final BlockingQueue<String> q = new LinkedBlockingQueue<String>();
		final InputStream is = read(f);
		final BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
		Thread t = new Thread("Files.concurrentLineReader:" + f) {
			@Override
            public void run() {
				String line;
				try {
					while ((line = br.readLine()) != null) {
						q.put(line);
					}
				} catch (final IOException e) {
				} catch (final InterruptedException e) {
				} finally {
					try {
						q.put(POISON_LINE);
						try {
							br.close();
							is.close();
						} catch (final IOException ee) {
						}
					} catch (final InterruptedException e) {
						// last try
						q.add(POISON_LINE);
						try {
							br.close();
							is.close();
						} catch (final IOException ee) {
						}
					}
				}
			}
		};
		t.start();
		return q;
	}

    /**
     * copy a file or a complete directory
     * @param from the source file or directory
     * @param to the destination file or directory
     * @throws IOException
     */
    public static void copy(final File from, final File to) throws IOException {
        if (!from.exists()) {
            throw new IOException("Can not find source: " + from.getAbsolutePath()+".");
        } else if (!from.canRead()) {
            throw new IOException("No right to source: " + from.getAbsolutePath()+".");
        }
        if (from.isDirectory())  {
            if (!to.exists() && !to.mkdirs()) {
                throw new IOException("Could not create directory: " + to.getAbsolutePath() + ".");
            }
            for (final String f : from.list()) {
                copy(new File(from, f) , new File(to, f));
            }
        } else {
            if (to.isDirectory()) throw new IOException("Cannot copy a file to an existing directory");
            if (to.exists()) to.delete();
            final byte[] buffer = new byte[4096];
            int bytesRead;
            InputStream in = null;
            OutputStream out = null;
            try {
            	in =  new BufferedInputStream(new FileInputStream(from));
            	out = new BufferedOutputStream(new FileOutputStream (to));
            	while ((bytesRead = in.read(buffer)) >= 0) {
            		out.write(buffer,0,bytesRead);
            	}
            } finally {
            	if(in != null) {
            		try {
            			in.close();
            		} catch(IOException ignored) {
            		}
            	}
            	if(out != null) {
            		out.close();
            	}
            }
        }
    }
}
