package org.prevayler.implementation;
import org.prevayler.foundation.FileManager;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;

public class PrevaylerDirectory {
  private static final int DIGITS_IN_FILENAME=19;
  private static final String SNAPSHOT_SUFFIX_PATTERN="[a-zA-Z0-9]*[Ss]napshot";
  private static final String SNAPSHOT_FILENAME_PATTERN="\\d{" + DIGITS_IN_FILENAME + "}\\."+ SNAPSHOT_SUFFIX_PATTERN;
  private static final String JOURNAL_SUFFIX_PATTERN="[a-zA-Z0-9]*[Jj]ournal";
  private static final String JOURNAL_FILENAME_PATTERN="\\d{" + DIGITS_IN_FILENAME + "}\\."+ JOURNAL_SUFFIX_PATTERN;
  private File _directory;
  public PrevaylerDirectory(  String directory){
    this(new File(directory));
  }
  public PrevaylerDirectory(  File directory){
    _directory=directory;
  }
  public void produceDirectory() throws IOException {
    FileManager.produceDirectory(_directory);
  }
  public static void checkValidSnapshotSuffix(  String suffix){
    if (!suffix.matches(SNAPSHOT_SUFFIX_PATTERN)) {
      throw new IllegalArgumentException("Snapshot filename suffix must match /" + SNAPSHOT_SUFFIX_PATTERN + "/, but '"+ suffix+ "' does not");
    }
  }
  public static void checkValidJournalSuffix(  String suffix){
    if (!suffix.matches(JOURNAL_SUFFIX_PATTERN)) {
      throw new IllegalArgumentException("Journal filename suffix must match /" + JOURNAL_SUFFIX_PATTERN + "/, but '"+ suffix+ "' does not");
    }
  }
  public File snapshotFile(  long version,  String suffix){
    checkValidSnapshotSuffix(suffix);
    return file(version,suffix);
  }
  public File journalFile(  long transaction,  String suffix){
    checkValidJournalSuffix(suffix);
    return file(transaction,suffix);
  }
  private File file(  long version,  String suffix){
    String fileName="0000000000000000000" + version;
    return new File(_directory,fileName.substring(fileName.length() - DIGITS_IN_FILENAME) + "." + suffix);
  }
  /** 
 * Returns -1 if fileName is not the name of a snapshot file.
 */
  public static long snapshotVersion(  File file){
    return version(file,SNAPSHOT_FILENAME_PATTERN);
  }
  /** 
 * Returns -1 if fileName is not the name of a journal file.
 */
  public static long journalVersion(  File file){
    return version(file,JOURNAL_FILENAME_PATTERN);
  }
  private static long version(  File file,  String filenamePattern){
    String fileName=file.getName();
    if (!fileName.matches(filenamePattern))     return -1;
    return Long.parseLong(fileName.substring(0,fileName.indexOf(".")));
  }
  /** 
 * Find the latest snapshot file. Returns null if no snapshot file was found.
 */
  public File latestSnapshot() throws IOException {
    File[] files=_directory.listFiles();
    if (files == null)     throw new IOException("Error reading file list from directory " + _directory);
    File latestSnapshot=null;
    long latestVersion=0;
    for (int i=0; i < files.length; i++) {
      File candidateSnapshot=files[i];
      long candidateVersion=snapshotVersion(candidateSnapshot);
      if (candidateVersion > latestVersion) {
        latestVersion=candidateVersion;
        latestSnapshot=candidateSnapshot;
      }
    }
    return latestSnapshot;
  }
  public File findInitialJournalFile(  long initialTransactionWanted){
    File[] journals=_directory.listFiles(new FileFilter(){
      public boolean accept(      File pathname){
        return pathname.getName().matches(JOURNAL_FILENAME_PATTERN);
      }
    }
);
    Arrays.sort(journals,new Comparator(){
      public int compare(      Object o1,      Object o2){
        File f1=(File)o1;
        File f2=(File)o2;
        return new Long(journalVersion(f1)).compareTo(new Long(journalVersion(f2)));
      }
    }
);
    for (int i=journals.length - 1; i >= 0; i--) {
      File journal=journals[i];
      long version=journalVersion(journal);
      if (version <= initialTransactionWanted) {
        return journal;
      }
    }
    return null;
  }
  public File createTempFile(  String prefix,  String suffix) throws IOException {
    return File.createTempFile(prefix,suffix,_directory);
  }
  public static void renameUnusedFile(  File journalFile){
    journalFile.renameTo(new File(journalFile.getAbsolutePath() + ".unusedFile" + System.currentTimeMillis()));
  }
}
