// $Id: IO.java,v 1.16 2004/12/15 00:11:46 caron Exp $
/*
 * Copyright 1997-2004 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 *
 * 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 library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package thredds.util;

import java.io.*;
import java.net.*;

/**
 * Input/Output utilities.
 *
 * @author John Caron
 * @version $Id: IO.java,v 1.16 2004/12/15 00:11:46 caron Exp $
 */
public class IO {

  static boolean showStackTrace = true;
  static boolean debug = false, showResponse = false;

  /**
   * copy all bytes from in to out.
   * @param in: InputStream
   * @param out: OutputStream
   * @throws IOException
   */
  static public void copy(InputStream in, OutputStream out) throws IOException {
      byte[] buffer = new byte[1024];
      while (true) {
        int bytesRead = in.read(buffer);
        if (bytesRead == -1) break;
        out.write(buffer, 0, bytesRead);
      }
  }

  /**
   * copy n bytes from in to out.
   * @param in: InputStream
   * @param out: OutputStream
   * @param n: number of bytes to copy
   * @throws IOException
   */
  static public void copy(InputStream in, OutputStream out, int n) throws IOException {
      byte[] buffer = new byte[1024];
      int count = 0;
      while (true) {
        int bytesRead = in.read(buffer);
        if (bytesRead == -1) break;
        out.write(buffer, 0, bytesRead);
        count += bytesRead;
        if (count > n) return;
      }
  }

   /**
    * copy one file to another.
    * @param fileInName: copy from this file, which must exist.
    * @param fileOutName: copy to this file, which is overrwritten if already exists.
    * @throws IOException
    */
  static public void copyFile(String fileInName, String fileOutName) throws IOException {

    InputStream in = new BufferedInputStream( new FileInputStream( fileInName));
    OutputStream out = new BufferedOutputStream( new FileOutputStream( fileOutName));
    thredds.util.IO.copy( in, out);
    in.close();
    out.close();
  }

   /**
    * copy one file to another.
    * @param fileIn: copy from this file, which must exist.
    * @param fileOut: copy to this file, which is overrwritten if already exists.
    * @throws IOException
    */
  static public void copyFile(File fileIn, File fileOut) throws IOException {
    InputStream in = new BufferedInputStream( new FileInputStream( fileIn));
    OutputStream out = new BufferedOutputStream( new FileOutputStream( fileOut));
    thredds.util.IO.copy( in, out);
    in.close();
    out.close();
  }

  /**
   * copy file to output stream
   * @param fileInName open this file
   * @param out : copy here
   * @throws IOException
   */
  static public void copyFile(String fileInName, OutputStream out) throws IOException {
    InputStream in = new BufferedInputStream( new FileInputStream( fileInName));
    thredds.util.IO.copy( in, out);
    in.close();
  }

  /**
   * copy entire directory tree
   * @param fromDirName from this directory (must exist)
   * @param toDirName to this directory (will create if not exist)
   * @throws IOException
   */
  static public void copyDirTree(String fromDirName, String toDirName) throws IOException {
    File fromDir = new File(fromDirName);
    File toDir = new File(toDirName);
    if (!fromDir.exists())
      throw new IOException(fromDirName+" does not exist");
    if (!toDir.exists())
      toDir.mkdirs();

    File[] files = fromDir.listFiles();
    for (int i=0; i<files.length; i++) {
      File f = files[i];
      if (f.isDirectory())
        copyDirTree(f.getAbsolutePath(), toDir.getAbsolutePath() + "/" + f.getName());
      else
        copyFile( f.getAbsolutePath(), toDir.getAbsolutePath() + "/" + f.getName());
    }

  }

  /**
   * Read the contents from the named URL and place into a String.
   * @param urlString: the URL to read from.
   * @return String holding the contents.
   * @throws IOException if fails
   */
  static public String readURLcontentsWithException( String urlString) throws IOException {

    URL url;
    try {
      url =  new URL( urlString);
    } catch (MalformedURLException e) {
      throw new IOException( "** MalformedURLException on URL <"+urlString+">\n"+e.getMessage()+"\n");
    }

    try {
      java.net.URLConnection connection = url.openConnection();

      if (connection instanceof HttpURLConnection) {
        java.net.HttpURLConnection httpConnection = (HttpURLConnection) connection;
        // check response code is good
        int responseCode = httpConnection.getResponseCode();
        if (responseCode/100 != 2)
          throw new IOException( "** Cant open URL <"+urlString+">\n Response code = " +responseCode
              +"\n"+httpConnection.getResponseMessage()+"\n");
      }

      // read it
      java.io.InputStream is = connection.getInputStream();
      ByteArrayOutputStream bout = new ByteArrayOutputStream(20000);
      thredds.util.IO.copy(is, bout);
      is.close();
      if (debug) System.out.println("readURLcontentsWithException success on "+urlString);

      return new String(bout.toByteArray());

    } catch (java.net.ConnectException e) {
        if (showStackTrace) e.printStackTrace();
        throw new IOException( "** ConnectException on URL: <"+urlString+">\n"+
          e.getMessage()+"\nServer probably not running");
    }
  }

  /**
   * Read the contents from the named URL and place into a String,
   * with any error messages  put in the return String.
   * @param urlString: the URL to read from.
   * @return String holding the contents, or an error message.
   */
  static public String readURLcontents( String urlString) {
    try {
      return readURLcontentsWithException( urlString);
    } catch (IOException e) {
      return e.getMessage();
    }
  }

  /**
   * Read the contents from the inputStream and place into a String,
   * with any error messages  put in the return String.
   * @param is : the inputStream to read from.
   * @return String holding the contents, or an error message.
   */
  static public String readContents( InputStream is) throws IOException {
    ByteArrayOutputStream bout = new ByteArrayOutputStream(1000);
    thredds.util.IO.copy(is, bout);
    return new String(bout.toByteArray());
  }

  /**
   * Read the contents from the named file and place into a String,
   * with any error messages  put in the return String.
   * @param filename: the URL to read from.
   * @return String holding the contents, or an error message.
   */
  static public String readFile( String filename) throws IOException {
    InputStream in = new BufferedInputStream( new FileInputStream( filename));
    return readContents (in);
  }

  /**
   * use HTTP PUT to send the contents to the named URL.
   * @param urlString : the URL to read from. must be http:
   * @param contents String holding the contents
   * @return a Result object; generally 0 <= code <=400 is ok
   */
  static public Result putToURL( String urlString, String contents) {

    URL url;
    try {
      url =  new URL( urlString);
    } catch (MalformedURLException e) {
      return new Result( -1, "** MalformedURLException on URL ("+urlString+")\n"+e.getMessage());
    }

    try {
      java.net.HttpURLConnection c = (HttpURLConnection) url.openConnection();
      c.setDoOutput(true);
      c.setRequestMethod("PUT");

      // read it
      OutputStream out = new BufferedOutputStream( c.getOutputStream());
      thredds.util.IO.copy( new ByteArrayInputStream(contents.getBytes()), out);
      out.flush();
      out.close();

      int code = c.getResponseCode();
      String mess = c.getResponseMessage();
      return new Result(code, mess);

    } catch (java.net.ConnectException e) {
        if (showStackTrace) e.printStackTrace();
        return new Result(-2, "** ConnectException on URL: <"+urlString+">\n"+e.getMessage()+"\nServer probably not running");

    } catch (IOException e) {
        if (showStackTrace) e.printStackTrace();
        return new Result(-3, "** IOException on URL: ("+urlString+")\n"+e.getMessage());
    }

  }

  static public class Result {
    public int code;
    public String message;
     Result( int code, String message) {
      this.code = code;
      this.message = message;
    }
  }

  /**
   * Read the contents from the inputStream and place into a String,
   * with any error messages  put in the return String.
   * @param contents String holding the contents.
   * @param os write to this OutputStream
   * @param contents String holding the contents
   */
  static public void writeContents( String contents, OutputStream os) throws IOException {
    ByteArrayInputStream bin = new ByteArrayInputStream( contents.getBytes());
    thredds.util.IO.copy(bin, os);
  }

  /**
   * Write contents to a file
   * @param contents String holding the contents
   * @param file  write to this file (overwrite if exists)
   * @param contents String holding the contents
   */
  static public void writeToFile( String contents, File file) throws IOException {
    OutputStream out = new BufferedOutputStream( new FileOutputStream( file));
    thredds.util.IO.copy( new ByteArrayInputStream(contents.getBytes()), out);
    out.flush();
    out.close();
  }

  /**
   * copy input stream to file.
   *
   * @param contents String holding the contents
   * @param fileOutName open this file (overwrite) and copy to it.
   * @param contents String holding the contents
   */
  static public void writeToFile(String contents, String fileOutName) throws IOException {
    InputStream in = new StringBufferInputStream(contents);
    OutputStream out = new BufferedOutputStream(new FileOutputStream(fileOutName));
    thredds.util.IO.copy(in, out);
    in.close();
    out.close();
  }

  /**
   * copy input stream to file.
   *
   * @param in          copy from here
   * @param fileOutName open this file (overwrite) and copy to it.
   * @throws IOException
   */
  static public void writeToFile(InputStream in, String fileOutName) throws IOException {
    OutputStream out = new BufferedOutputStream(new FileOutputStream(fileOutName));
    thredds.util.IO.copy(in, out);
    in.close();
    out.close();
  }

}