/*
 * Copyright (C) 2021 - 2024 ScalAgent Distributed Technologies
 *
 * 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 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.
 *
 * Initial developer(s): ScalAgent Distributed Technologies
 * Contributor(s): 
 */
package fr.dyade.aaa.util.backup;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.regex.Pattern;

import fr.dyade.aaa.common.BinaryDump;

/**
 * Class allowing to handles a backup file.
 */
public class RestoreFile extends RandomAccessFile implements Closeable {
  private int nbRecords;

  /**
   * Number of objects recorded in the file.
   * @return the number of objects recorded in the file.
   */
  public int getNbRecords() {
    return nbRecords;
  }
  
  /**
   * Opens a backup file to restore it.
   * 
   * @param file  The file to restore.
   * @throws IOException  An error occurs.
   */
  public RestoreFile(File file) throws IOException {
    super(file, "r");
    // Reads the total number of record
    nbRecords = readInt();
    if (nbRecords < 0)
      throw new IOException("Backup file is not finalized");
  }
  
  private int nbRead = 0;
  
  /**
   * Reads the next record in file.
   * @return  the next record in file.
   * @throws IOException  An error occurs.
   */
  public BackupRecord getNextRecord() throws IOException {
    BackupRecord record = null;
    if (nbRead < nbRecords) {
      nbRead += 1;
      record = new BackupRecord();
      record.dirName = readUTF();
      if (record.dirName.length() == 0)
        record.dirName = null;
      record.name = readUTF();
      int length = readInt();
      record.value = new byte[length];
      readFully(record.value);
    }
    return record;
  }
  
  /**
   * Lists the objects contained in the backup file.
   * @param file    The backup file.
   * @param filter  The filter.
   * @throws IOException  An error occurs.
   */
  public static void list(File file, Pattern filter) throws IOException {
    RestoreFile restoreFile = null;
    try {
      restoreFile = new RestoreFile(file);
      System.out.println("| RestoreFile: " + restoreFile.nbRecords + "\n+---");
      
      BackupRecord record = restoreFile.getNextRecord();
      while (record != null) {
        if ((filter == null) || filter.matcher(record.getName()).matches())
          System.out.println("| " + record);
        record = restoreFile.getNextRecord();
      }
      System.out.println("+---");
    } finally {
      if (restoreFile != null)
        restoreFile.close();
    }
  }
  
  /**
   * Extracts the objects contained in the backup file to the specified directory.
   * 
   * @param backupFile  The backup file.
   * @param filter      The filter.
   * @param extractDir  The directory where to extract the data.
   * @throws IOException  An error occurs.
   */
  public static void extract(File backupFile, Pattern filter, File extractDir) throws IOException {
    RestoreFile restoreFile = null;
    try {
      restoreFile = new RestoreFile(backupFile);
      System.out.println("| RestoreFile: " + restoreFile.nbRecords + "\n+---");
      
      BackupRecord record = restoreFile.getNextRecord();
      while (record != null) {
        if ((filter == null) || filter.matcher(record.getName()).matches()) {
          System.out.println("| Restores " + record.getPathname());
          File dir = extractDir;
          if (record.dirName != null) {
            dir = new File(extractDir, record.dirName);
            if (!dir.mkdirs())
              throw new IOException("can't create directory " + record.dirName);
          }
          File file = new File(dir, record.name);
          RandomAccessFile raf = null;
          try {
            raf = new RandomAccessFile(file, "rw");
            raf.write(record.value);
          } finally {
            if (raf != null) raf.close();
          }
        }
        record = restoreFile.getNextRecord();
      }
      System.out.println("+---");
    } finally {
      if (restoreFile != null) restoreFile.close();
    }
  }
  
  /**
   * Dumps the objects contained in the backup file.
   * @param file    The backup file.
   * @param filter  The filter.
   * @throws IOException  An error occurs.
   */
  public static void dump(File file, Pattern filter) throws IOException {
    RestoreFile restoreFile = null;
    try {
      restoreFile = new RestoreFile(file);
      System.out.println("| RestoreFile: " + restoreFile.nbRecords);
      
      BackupRecord record = restoreFile.getNextRecord();
      while (record != null) {
        if ((filter == null) || filter.matcher(record.getName()).matches()) {
          System.out.println("+---\n| Dumps " +  record.getPathname());
          System.out.print(BinaryDump.dump(record.getValue(), 0, 0));
        }
        record = restoreFile.getNextRecord();
      }
      System.out.println("+---");
    } finally {
      if (restoreFile != null)
        restoreFile.close();
    }
  }
}
