/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/util/org/apache/slide/util/IO.java,v 1.1 2004/03/25 16:18:11 juergen Exp $
 * $Revision: 1.1 $
 * $Date: 2004/03/25 16:18:11 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * 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 org.apache.slide.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
import org.apache.slide.util.nodeset.NodeSet;
import org.apache.slide.util.nodeset.Task;

public class IO {
    private final byte[] buffer;
    
    private final String encoding;
    
    private final String backupExtension;
    
    public static final String DEFAULT_ENCODING;
    
    static {
        DEFAULT_ENCODING = System.getProperty("file.encoding");
        if (DEFAULT_ENCODING == null) {
            throw new IllegalStateException("no default encoding");
        }
    }
    
    
    public IO() {
        this(8192, DEFAULT_ENCODING);
    }
    
    public IO(int bufferSize, String encoding) {
        this.buffer = new byte[bufferSize];
        this.encoding = encoding;
        this.backupExtension = ".bak";
    }
    
    public void write(File file, String txt) throws IOException {
        Writer w;
        
        w = new OutputStreamWriter(new FileOutputStream(file), encoding);
        w.write(txt);
        w.close();
    }
    
    public void write(File file, byte[] bytes) throws IOException {
        OutputStream out;
        
        out = new FileOutputStream(file);
        out.write(bytes);
        out.close();
    }
    
    //--
    
    public String readString(File file) throws IOException {
        byte[] bytes;
        
        bytes = readBytes(file);
        return new String(bytes, encoding);
    }
    
    public String readString(InputStream src) throws IOException {
        byte[] bytes;
        
        bytes = readBytes(src);
        return new String(bytes, encoding);
    }
    
    public byte[] readBytes(InputStream src) throws IOException {
        ByteArrayOutputStream dest;
        
        dest = new ByteArrayOutputStream();
        copy(src, dest);
        return dest.toByteArray();
    }
    
    public byte[] readBytes(File file) throws IOException {
        long length;
        InputStream src;
        byte[] byteBuffer;
        
        length = file.length();
        if (length > Integer.MAX_VALUE) {
            throw new IOException("file to big: " + length);
        }
        byteBuffer = new byte[(int) length];
        src = new FileInputStream(file);
        src.read(byteBuffer);
        src.close();
        
        return byteBuffer;
    }
    
    
    /**
     * Does not copy directories
     *
     * @param srcfile  existing file
     * @param destfile non-existing file
     */
    /**
     * Method copy
     *
     * @param    srcfile             a  File
     * @param    destfile            a  File
     *
     * @throws   IOException
     *
     */
    public void copy(File srcfile, File destfile) throws IOException {
        InputStream src;
        OutputStream dest;
        long modified;
        long foundModified;
        
        src = new FileInputStream(srcfile);
        dest = new FileOutputStream(destfile);
        copy(src, dest);
        src.close();
        dest.close();
        
        modified = srcfile.lastModified();
        if (!destfile.setLastModified(modified)) {
            // I've seen failures one Windows machines operating on Samba Shares ...
            // Ant 1.5.4 uses setLastModified exactly like we do - but it does not
            // complain if false is returned.
            foundModified = destfile.lastModified();
            if (foundModified == modified) {
                // TODO: does this every happen?
                System.out.println("warning: setLastModified failed, but modification date was changed");
            } else {
                System.out.println("warning: setLastModified failed: " + destfile +
                                       "  (diff: " + 0.001 * Math.abs(foundModified - modified) + " sec)");
            }
        }
    }
    
    public void copy(InputStream in, OutputStream out) throws IOException {
        int len;
        
        while (true) {
            len = in.read(buffer);
            if (len < 0) {
                break;
            }
            out.write(buffer, 0, len);
        }
        out.flush();
    }
    
    //--
    
    public void copy(final File srcdir, final File destdir, NodeSet set) throws IOException {
        if (Files.getRelative(srcdir, destdir) != null) {
            throw new IllegalArgumentException("src contains dest");
        }
        Files.checkDirectory(srcdir);
        Files.checkDirectory(destdir);
        set.invoke(srcdir, new Task() {
                    public void invoke(Object srcObj) throws IOException {
                        File src;
                        File dest;
                        
                        src = (File) srcObj;
                        if (srcdir.equals(src)) {
                            // nothing to do
                        } else {
                            dest = new File(destdir, Files.getRelative(srcdir, src));
                            if (src.isDirectory()) {
                                Files.mkdir(dest);
                            } else {
                                copy(src, dest);
                            }
                        }
                    }
                });
    }
    
    
    //--
 
    public void backup(File src) throws IOException {
        File dest;
        
        dest = new File(src.getPath() + backupExtension);
        if (dest.isFile()) {
            Files.delete(dest);
        }
        copy(src, dest);
    }
    
    /** TODO: this is quite memory consuming ...  */
    public boolean equals(File left, File right) throws IOException {
        if (left.length() != right.length()) {
            return false;
        }
        return Arrays.equals(readBytes(left), readBytes(right));
    }
}
