/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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.geode.internal.cache.persistence;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.geode.internal.lang.SystemUtils;

/**
 * This class is used to automatically generate a restore script for a backup. It keeps a list of
 * files that were backed up, and a list of files that we should test for to avoid overriding when
 * we restore the backup.
 * 
 * <p>
 * It generates either a restore.sh for unix or a restore.bat for windows.
 */
public class RestoreScript {

  static final String INCREMENTAL_MARKER_COMMENT =
      "Incremental backup.  Restore baseline originals from previous backups.";

  static final String REFUSE_TO_OVERWRITE_MESSAGE = "Backup not restored. Refusing to overwrite ";

  private static final String[] ABOUT_SCRIPT_COMMENT =
      {"Restore a backup of gemfire persistent data to the location it was backed up",
          "from. This script will refuse to restore if the original data still exists.",
          "This script was automatically generated by the gemfire backup utility.",};

  private static final String EXISTENCE_CHECK_COMMENT =
      "Test for existing originals.  If they exist, do not restore the backup.";

  private static final String RESTORE_DATA_COMMENT = "Restore data";

  private final ScriptGenerator generator;
  private final Map<File, File> baselineFiles = new HashMap<File, File>();
  private final Map<File, File> backedUpFiles = new LinkedHashMap<File, File>();
  private final List<File> existenceTests = new ArrayList<File>();

  private PrintWriter writer;
  private File outputDir;

  public RestoreScript() {
    this(SystemUtils.isWindows() ? new WindowsScriptGenerator() : new UnixScriptGenerator());
  }

  RestoreScript(final ScriptGenerator generator) {
    this.generator = generator;
  }

  public void addBaselineFiles(final Map<File, File> baselineFiles) {
    this.baselineFiles.putAll(baselineFiles);
  }

  public void addFile(final File originalFile, final File backupFile) {
    backedUpFiles.put(backupFile, originalFile.getAbsoluteFile());
  }

  public void addExistenceTest(final File originalFile) {
    existenceTests.add(originalFile.getAbsoluteFile());
  }

  public void generate(final File outputDir) throws FileNotFoundException {
    this.outputDir = outputDir;
    File outputFile = new File(outputDir, generator.getScriptName());
    generateScript(outputFile);
  }

  private void generateScript(final File outputFile) throws FileNotFoundException {
    writer = new PrintWriter(outputFile);
    try {

      writePreamble();
      writeAbout();
      writeExistenceTest();
      writeRestoreData();
      writeIncrementalData();
      generator.writeExit(writer);

    } finally {
      writer.close();
    }

    outputFile.setExecutable(true, true);
  }

  private void writePreamble() {
    generator.writePreamble(writer);
    writer.println();
  }

  private void writeAbout() {
    for (String comment : ABOUT_SCRIPT_COMMENT) {
      generator.writeComment(writer, comment);
    }
    writer.println();
  }

  private void writeExistenceTest() {
    generator.writeComment(writer, EXISTENCE_CHECK_COMMENT);
    for (File file : existenceTests) {
      generator.writeExistenceTest(writer, file);
    }
    writer.println();
  }

  private void writeRestoreData() {
    generator.writeComment(writer, RESTORE_DATA_COMMENT);
    for (Map.Entry<File, File> entry : backedUpFiles.entrySet()) {
      File backup = entry.getKey();
      boolean backupHasFiles = backup.isDirectory() && backup.list().length != 0;
      backup = outputDir.toPath().relativize(backup.toPath()).toFile();
      File original = entry.getValue();
      if (original.isDirectory()) {
        generator.writeCopyDirectoryContents(writer, backup, original, backupHasFiles);
      } else {
        generator.writeCopyFile(writer, backup, original);
      }
    }
  }

  private void writeIncrementalData() {
    // Write out baseline file copies in restore script (if there are any) if this is a restore
    // for an incremental backup
    if (this.baselineFiles.isEmpty()) {
      return;
    }

    writer.println();
    generator.writeComment(writer, INCREMENTAL_MARKER_COMMENT);
    for (Map.Entry<File, File> entry : this.baselineFiles.entrySet()) {
      generator.writeCopyFile(writer, entry.getKey(), entry.getValue());
    }
  }
}
