/*
 * $Id: IOUtils.java 9120 2014-06-26 19:45:54Z uckelman $
 *
 * Copyright (c) 2007-2010 by Joel Uckelman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available
 * at http://www.opensource.org.
 */
package VASSAL.tools.io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.util.zip.ZipFile;

import javax.imageio.stream.ImageInputStream;

/**
 * General I/O stream manipulation utilities. This class provides static
 * utility methods to reduce boilerplate I/O code.
 *
 * @author Joel Uckelman
 * @since 3.1.0
 */
public class IOUtils extends org.apache.commons.io.IOUtils {
  protected IOUtils() {}

  /**
   * Copies bytes from a <code>FileInputStream</code> to a
   * <code>FileOutputStream</code>.
   *
   * This method uses channels. The input file should not be written
   * to during the copy.
   *
   * @param in the source
   * @param out the destination
   * @throws IOException if one occurs while reading or writing
   */
  public static int copy(FileInputStream in, FileOutputStream out)
                                                           throws IOException {
    final long count = copyLarge(in, out);
    return count > Integer.MAX_VALUE ? -1 : (int) count;
  }

  /**
   * Copies bytes from a large (over 2GB) <code>FileInputStream</code> to a
   * <code>FileOutputStream</code>.
   *
   * This method uses channels. The input file should not be written
   * to during the copy.
   *
   * @param in the source
   * @param out the destination
   * @throws IOException if one occurs while reading or writing
   */
  public static long copyLarge(FileInputStream in, FileOutputStream out)
                                                           throws IOException {
    final FileChannel inc = in.getChannel();
    return inc.transferTo(0L, inc.size(), out.getChannel());
  }

  /**
   * Copies bytes from an <code>InputStream</code> to an
   * <code>OutputStream</code> via a <code>byte</code> buffer. This
   * method buffers input internally, so the input stream should not
   * be a <code>BufferedInputStream</code>.
   *
   * @param in the source
   * @param out the destination
   * @param buffer the buffer
   * @return the number of bytes copied
   * @throws IOException if one occurs while reading or writing
   */
  public static int copy(InputStream in, OutputStream out, byte[] buffer)
                                                           throws IOException {
    final long count = copyLarge(in, out, buffer);
    return count > Integer.MAX_VALUE ? -1 : (int) count;
  }

  /**
   * Copies bytes from a large (over 2GB) <code>InputStream</code> to an
   * <code>OutputStream</code> via a <code>byte</code> buffer. This
   * method buffers input internally, so the input stream should not
   * be a <code>BufferedInputStream</code>.
   *
   * @param in the source
   * @param out the destination
   * @param buffer the buffer
   * @return the number of bytes copied
   * @throws IOException if one occurs while reading or writing
   */
  public static long copyLarge(InputStream in, OutputStream out, byte[] buffer)
                                                           throws IOException {
    long count = 0;
    int n = 0;
    while ((n = in.read(buffer)) != -1) {
      out.write(buffer, 0, n);
      count += n;
    }
    return count;
  }

  /**
   * Close an {@link ObjectInput} unconditionally. Equivalent to
   * calling <code>o.close()</code> when <code>o</code> is nonnull.
   * {@link IOException}s are swallowed, as there is generally
   * nothing that can be done about exceptions on closing.
   *
   * @param o a (possibly <code>null</code>) <code>ObjectInput</code>
   */
  public static void closeQuietly(ObjectInput o) {
    if (o == null) return;

    try {
      o.close();
    }
    catch (IOException e) {
      // ignore
    }
  }

  /**
   * Close an {@link ObjectOutput} unconditionally. Equivalent to
   * calling <code>o.close()</code> when <code>o</code> is nonnull.
   * {@link IOException}s are swallowed, as there is generally
   * nothing that can be done about exceptions on closing.
   *
   * @param o a (possibly <code>null</code>) <code>ObjectOutput</code>
   */
  public static void closeQuietly(ObjectOutput o) {
    if (o == null) return;

    try {
      o.close();
    }
    catch (IOException e) {
      // ignore
    }
  }

  /**
   * Close a {@link ZipFile} unconditionally. Equivalent to
   * calling <code>z.close()</code> when <code>z</code> is nonnull.
   * {@link IOException}s are swallowed, as there is generally
   * nothing that can be done about exceptions on closing.
   *
   * @param z a (possibly <code>null</code>) <code>ZipFile</code>
   */
  // Why doesn't ZipFile implement Closeable? Argh!
  public static void closeQuietly(ZipFile z) {
    if (z == null) return;

    try {
      z.close();
    }
    catch (IOException e) {
      // ignore
    }
  }

  /**
   * Close an {@link ImageInputStream} unconditionally. Equivalent to
   * calling <code>s.close()</code> when <code>s</code> is nonnull.
   * {@link IOException}s are quietly logged, as there is generally
   * nothing that can be done about exceptions on closing.
   *
   * @param s a (possibly <code>null</code>) <code>ImageInputStream</code>
   */
  // Why doesn't ImageInputStream implement Closeable? Argh!
  public static void closeQuietly(ImageInputStream s) {
    if (s == null) return;

    try {
      s.close();
    }
    catch (IOException e) {
      // ignore

      // Note that ImageInputStreamImpl.close() rather ridiculously throws
      // an IOException if the stream is already closed. This is always done
      // via ImageInputStreamImpl.checkClosed().
    }
  }
}