/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 et: */
package jp.oist.flint.filesystem;

import org.apache.log4j.Logger;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.UUID;

public class Workspace {

    private static final String LOCK_FILE_NAME = "flint.lock";
    private static final String LOG_FILE_NAME = "flint.log";

    private final static String WORKSPACE_NAME;

    private static File mLockFile;
    private static FileLock mFileLock; 

    static {
        WORKSPACE_NAME = new File(".flint", UUID.randomUUID().toString()).getPath();

        try {
            File dir = getFile();
            dir.mkdirs();
            mLockFile = new File(dir, LOCK_FILE_NAME);
            if (!mLockFile.exists()) mLockFile.createNewFile();
            mLockFile.deleteOnExit();
            FileOutputStream lockStream = new FileOutputStream(mLockFile);
            FileChannel lockChannel = lockStream.getChannel();
            mFileLock = lockChannel.tryLock();

            clear();
        } catch (IOException ioe) {
            // ignored.
        } finally {
            if (mFileLock == null) {
                Logger.getRootLogger().fatal("could not get lock: " + mLockFile.toString());
                System.exit(1);
            }
        }

        Runtime.getRuntime().addShutdownHook(new Thread () {
            @Override
            public void run() {
                if (mFileLock != null) {
                    try {
                        FileChannel channel = mFileLock.channel();
                        mFileLock.release();
                        channel.close();

                        Workspace.recursiveDelete(Workspace.getFile());
                    } catch (IOException ex) {
                        Logger.getRootLogger().error(ex.getMessage());
                    }
                }
            }
        });
    }

    private static void clear() throws IOException {
        File lockFile = null;
        FileLock lock = null;
        FileChannel lockChannel = null;
        try {
            File flintWorkspace = getFile().getParentFile();

            lockFile = new File(flintWorkspace, "flint.lock");
            if (!lockFile.exists())
                lockFile.createNewFile();

            FileOutputStream lockStream = new FileOutputStream(lockFile);
            lockChannel = lockStream.getChannel();

            lock = lockChannel.tryLock();
            if (lock == null)
                throw new IOException("could not create the lock file");

            for (File child : flintWorkspace.listFiles()) {
                if (getFile().equals(child) || lockFile.equals(child))
                        continue;

                FileChannel instanceChannel = null;
                try {
                    File instanceLockFile = new File(child, "flint.lock");

                    if (instanceLockFile.exists()) {
                        FileLock instanceLock = null;
                        try {
                            FileOutputStream instanceStream 
                                    = new FileOutputStream(instanceLockFile);
                            instanceChannel = instanceStream.getChannel();
                            instanceLock = instanceChannel.tryLock();
                            if (instanceLock == null)
                                continue;
                        } finally {
                            if (instanceLock != null)
                                instanceLock.release();

                            if (instanceChannel != null)
                                instanceChannel.close();
                        }
                    }

                    recursiveDelete(child);
                } catch (IOException ex) {
                    Logger.getRootLogger().error(ex.getMessage());
                } finally {
                }
            }
        } catch (IOException ex) {
            Logger.getRootLogger().error("could not create the lock file.");
            return;
        } finally {
            if (lock != null)
                lock.release();
            if (lockChannel != null)
                lockChannel.close();
            if (lockFile != null && lockFile.exists())
                lockFile.delete();
        }
    }

    private static File getFile() throws IOException {
        return new File(getPath());
    }

    // TODO: This method name becomes a misnomer, since Java 7 introduces java.nio.file.Path.
    public static String getPath() throws IOException {
        return System.getProperty("user.home") + File.separator + WORKSPACE_NAME;
    }

    public static File createTempFile(String prefix, String suffix) throws IOException {
        // Prefix must be at least three characters long
        // See
        // http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#createTempFile(java.nio.file.Path,%20java.lang.String,%20java.lang.String,%20java.nio.file.attribute.FileAttribute...)
        // and
        // http://docs.oracle.com/javase/7/docs/api/java/io/File.html#createTempFile(java.lang.String,%20java.lang.String,%20java.io.File)
        while (prefix.length() < 3) {
            prefix += "_";
        }
        Path path = Files.createTempFile(getFile().toPath(), prefix, suffix);
        return path.toFile();
    }

    public static File createTempDirectory(String prefix) throws IOException {
        Path path = Files.createTempDirectory(getFile().toPath(), prefix);
        return path.toFile();
    }

    public static void publishFile(File source, File target) throws IOException {
        Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
    }

    public static void recursiveDelete (File target) {
        if (target  == null)
            return;

        if (target.isDirectory()) {
            for (File f : target.listFiles())
                recursiveDelete (f);
        }

        target.delete();
    }

    public static class ShutdownHook implements Runnable {

        private ShutdownHook() { }

        @Override
        public void run() {
            if (mFileLock != null) {
                try {
                    FileChannel channel = mFileLock.channel();
                    mFileLock.release();
                    channel.close();

                    Workspace.recursiveDelete(Workspace.getFile());
                } catch (IOException ex) {
                    // ignored.
                }
            }
        }
    }
}

@@ -1577,548 +1577,22 @@
 new 
-Thread () %7B%0A            @Override%0A            public void run() %7B%0A                if (mFileLock != null) %7B%0A                    try %7B%0A                        FileChannel channel = mFileLock.channel();%0A                        mFileLock.release();%0A                        channel.close();%0A%0A                        Workspace.recursiveDelete(Workspace.getFile());%0A                    %7D catch (IOException ex) %7B%0A                        Logger.getRootLogger().error(ex.getMessage());%0A                    %7D%0A                %7D%0A            %7D%0A        %7D
+ShutdownHook()
 );%0A 
@@ -5665,37 +5665,38 @@
 ();%0A    %7D%0A%0A    p
-ublic
+rivate
  static class Sh
@@ -5710,65 +5710,24 @@
 ook 
-implements Runnable %7B%0A%0A        private ShutdownHook() %7B %7D
+extends Thread %7B
 %0A%0A  
@@ -5827,38 +5827,17 @@
     try 
-%7B%0A                    
+(
 FileChan
@@ -5861,33 +5861,35 @@
 leLock.channel()
-;
+) %7B
 %0A               
@@ -5897,66 +5897,55 @@
     
-mFileLock.release();%0A                    channel.clos
+Workspace.recursiveDelete(Workspace.getFil
 e()
+)
 ;
-%0A
 %0A   
@@ -5965,54 +5965,27 @@
     
-Workspace.recursiveDelete(Workspace.getFil
+mFileLock.releas
 e()
-)
 ;%0A  
@@ -6011,34 +6011,35 @@
 ch (IOException 
+io
 e
-x
 ) %7B%0A            
@@ -6048,33 +6048,32 @@
       // ignored
-.
 %0A               

remove duplicate code