/*
 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
 * Version 1.0, and under the Eclipse Public License, Version 1.0
 * (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.test.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileSystem;
import org.h2.store.fs.FileSystemWrapper;

/**
 * A debugging file system that logs all operations.
 */
public class DebugFileSystem extends FileSystemWrapper {

    /**
     * The prefix used for a debugging file system.
     */
    static final String PREFIX = "debug:";

    private static final DebugFileSystem INSTANCE = new DebugFileSystem();

    private static final IOException POWER_OFF = new IOException("Simulated power failure");

    private int powerOffCount;
    private boolean trace;

    /**
     * Register the file system.
     *
     * @return the instance
     */
    public static DebugFileSystem register() {
        FileSystem.register(INSTANCE);
        return INSTANCE;
    }

    /**
     * Check if the simulated power failure occurred.
     * This call will decrement the countdown.
     *
     * @throws IOException if the simulated power failure occurred
     */
    void checkPowerOff() throws IOException {
        if (powerOffCount == 0) {
            return;
        }
        if (powerOffCount > 1) {
            powerOffCount--;
            return;
        }
        powerOffCount = -1;
        // throw new IOException("Simulated power failure");
        throw POWER_OFF;
    }

    public boolean canWrite(String fileName) {
        trace(fileName, "canWrite");
        return super.canWrite(fileName);
    }

    public void copy(String source, String target) {
        trace(source, "copy", unwrap(target));
        super.copy(source, target);
    }

    public void createDirs(String fileName) {
        trace(fileName, "createDirs");
        super.createDirs(fileName);
    }

    public boolean createNewFile(String fileName) {
        trace(fileName, "createNewFile");
        return super.createNewFile(fileName);
    }

    public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
            throws IOException {
        trace(prefix, "createTempFile", suffix, deleteOnExit, inTempDir);
        return super.createTempFile(prefix, suffix, deleteOnExit, inTempDir);
    }

    public void delete(String fileName) {
        trace(fileName, "fileName");
        super.delete(fileName);
    }

    public void deleteRecursive(String directory, boolean tryOnly) {
        trace(directory, "deleteRecursive", tryOnly);
        super.deleteRecursive(directory, tryOnly);
    }

    public boolean exists(String fileName) {
        trace(fileName, "exists");
        return super.exists(fileName);
    }

    public boolean fileStartsWith(String fileName, String prefix) {
        trace(fileName, "fileStartsWith", unwrap(prefix));
        return super.fileStartsWith(fileName, prefix);
    }

    public String getAbsolutePath(String fileName) {
        trace(fileName, "getAbsolutePath");
        return super.getAbsolutePath(fileName);
    }

    public String getFileName(String name) {
        trace(name, "getFileName");
        return super.getFileName(name);
    }

    public long getLastModified(String fileName) {
        trace(fileName, "getLastModified");
        return super.getLastModified(fileName);
    }

    public String getParent(String fileName) {
        trace(fileName, "getParent");
        return super.getParent(fileName);
    }

    public boolean isAbsolute(String fileName) {
        trace(fileName, "isAbsolute");
        return super.isAbsolute(fileName);
    }

    public boolean isDirectory(String fileName) {
        trace(fileName, "isDirectory");
        return super.isDirectory(fileName);
    }

    public boolean isReadOnly(String fileName) {
        trace(fileName, "isReadOnly");
        return super.isReadOnly(fileName);
    }

    public boolean setReadOnly(String fileName) {
        trace(fileName, "setReadOnly");
        return super.setReadOnly(fileName);
    }

    public long length(String fileName) {
        trace(fileName, "length");
        return super.length(fileName);
    }

    public String[] listFiles(String directory) {
        trace(directory, "listFiles");
        return super.listFiles(directory);
    }

    public String normalize(String fileName) {
        trace(fileName, "normalize");
        return super.normalize(fileName);
    }

    public InputStream openFileInputStream(String fileName) throws IOException {
        trace(fileName, "openFileInputStream");
        return super.openFileInputStream(fileName);
    }

    public FileObject openFileObject(String fileName, String mode) throws IOException {
        trace(fileName, "openFileObject", mode);
        return new DebugFileObject(this, super.openFileObject(fileName, mode));
    }

    public OutputStream openFileOutputStream(String fileName, boolean append) {
        trace(fileName, "openFileOutputStream", append);
        return super.openFileOutputStream(fileName, append);
    }

    public void rename(String oldName, String newName) {
        trace(oldName, "rename", unwrap(newName));
        super.rename(oldName, newName);
    }

    public boolean tryDelete(String fileName) {
        trace(fileName, "tryDelete");
        return super.tryDelete(fileName);
    }

    public String getPrefix() {
        return PREFIX;
    }

    /**
     * Print a debug message.
     *
     * @param fileName the (wrapped) file name
     * @param method the method name
     * @param params parameters if any
     */
    void trace(String fileName, String method, Object... params) {
        if (trace) {
            StringBuilder buff = new StringBuilder("    ");
            buff.append(unwrap(fileName)).append(' ').append(method);
            for (Object s : params) {
                buff.append(' ').append(s);
            }
            System.out.println(buff);
        }
    }

    public void setPowerOffCount(int count) {
        this.powerOffCount = count;
    }

    public int getPowerOffCount() {
        return powerOffCount;
    }

    public boolean isTrace() {
        return trace;
    }

    public void setTrace(boolean trace) {
        this.trace = trace;
    }

}
