package org.apache.logging.log4j.core.appender.rolling;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.appender.rolling.action.Action;
import org.apache.logging.log4j.core.appender.rolling.action.FileRenameAction;
import org.apache.logging.log4j.core.appender.rolling.action.GzCompressAction;
import org.apache.logging.log4j.core.appender.rolling.action.ZipCompressAction;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.apache.logging.log4j.core.util.Integers;
import org.apache.logging.log4j.status.StatusLogger;

@Plugin(
   name = "DefaultRolloverStrategy",
   category = "Core",
   printObject = true
)
public class DefaultRolloverStrategy implements RolloverStrategy {
   private static final String EXT_ZIP = ".zip";
   private static final String EXT_GZIP = ".gz";
   protected static final Logger LOGGER = StatusLogger.getLogger();
   private static final int MIN_WINDOW_SIZE = 1;
   private static final int DEFAULT_WINDOW_SIZE = 7;
   private final int maxIndex;
   private final int minIndex;
   private final boolean useMax;
   private final StrSubstitutor subst;
   private final int compressionLevel;

   @PluginFactory
   public static DefaultRolloverStrategy createStrategy(@PluginAttribute("max") String max, @PluginAttribute("min") String min, @PluginAttribute("fileIndex") String fileIndex, @PluginAttribute("compressionLevel") String compressionLevelStr, @PluginConfiguration Configuration config) {
      boolean useMax = fileIndex == null ? true : fileIndex.equalsIgnoreCase("max");
      int minIndex = 1;
      if (min != null) {
         minIndex = Integer.parseInt(min);
         if (minIndex < 1) {
            LOGGER.error("Minimum window size too small. Limited to 1");
            minIndex = 1;
         }
      }

      int maxIndex = 7;
      if (max != null) {
         maxIndex = Integer.parseInt(max);
         if (maxIndex < minIndex) {
            maxIndex = minIndex < 7 ? 7 : minIndex;
            LOGGER.error("Maximum window size must be greater than the minimum windows size. Set to " + maxIndex);
         }
      }

      int compressionLevel = Integers.parseInt(compressionLevelStr, -1);
      return new DefaultRolloverStrategy(minIndex, maxIndex, useMax, compressionLevel, config.getStrSubstitutor());
   }

   protected DefaultRolloverStrategy(int minIndex, int maxIndex, boolean useMax, int compressionLevel, StrSubstitutor subst) {
      this.minIndex = minIndex;
      this.maxIndex = maxIndex;
      this.useMax = useMax;
      this.compressionLevel = compressionLevel;
      this.subst = subst;
   }

   public int getCompressionLevel() {
      return this.compressionLevel;
   }

   public int getMaxIndex() {
      return this.maxIndex;
   }

   public int getMinIndex() {
      return this.minIndex;
   }

   private int purge(int lowIndex, int highIndex, RollingFileManager manager) {
      return this.useMax ? this.purgeAscending(lowIndex, highIndex, manager) : this.purgeDescending(lowIndex, highIndex, manager);
   }

   private int purgeAscending(int lowIndex, int highIndex, RollingFileManager manager) {
      int suffixLength = 0;
      List<FileRenameAction> renames = new ArrayList();
      StringBuilder buf = new StringBuilder();
      manager.getPatternProcessor().formatFileName(this.subst, buf, highIndex);
      String highFilename = this.subst.replace(buf);
      if (highFilename.endsWith(".gz")) {
         suffixLength = ".gz".length();
      } else if (highFilename.endsWith(".zip")) {
         suffixLength = ".zip".length();
      }

      int maxIndex = 0;

      int i;
      for(i = highIndex; i >= lowIndex; --i) {
         File toRename = new File(highFilename);
         if (i == highIndex && toRename.exists()) {
            maxIndex = highIndex;
         } else if (maxIndex == 0 && toRename.exists()) {
            maxIndex = i + 1;
            break;
         }

         boolean isBase = false;
         if (suffixLength > 0) {
            File toRenameBase = new File(highFilename.substring(0, highFilename.length() - suffixLength));
            if (toRename.exists()) {
               if (toRenameBase.exists()) {
                  LOGGER.debug("DefaultRolloverStrategy.purgeAscending deleting {} base of {}.", toRenameBase, toRename);
                  toRenameBase.delete();
               }
            } else {
               toRename = toRenameBase;
               isBase = true;
            }
         }

         if (toRename.exists()) {
            if (i == lowIndex) {
               LOGGER.debug("DefaultRolloverStrategy.purgeAscending deleting {} at low index {}: all slots full.", toRename, i);
               if (!toRename.delete()) {
                  return -1;
               }
               break;
            }

            buf.setLength(0);
            manager.getPatternProcessor().formatFileName(this.subst, buf, i - 1);
            String lowFilename = this.subst.replace(buf);
            String renameTo = lowFilename;
            if (isBase) {
               renameTo = lowFilename.substring(0, lowFilename.length() - suffixLength);
            }

            renames.add(new FileRenameAction(toRename, new File(renameTo), true));
            highFilename = lowFilename;
         } else {
            buf.setLength(0);
            manager.getPatternProcessor().formatFileName(this.subst, buf, i - 1);
            highFilename = this.subst.replace(buf);
         }
      }

      if (maxIndex == 0) {
         maxIndex = lowIndex;
      }

      for(i = renames.size() - 1; i >= 0; --i) {
         Action action = (Action)renames.get(i);

         try {
            LOGGER.debug("DefaultRolloverStrategy.purgeAscending executing {} of {}: {}", i, renames.size(), action);
            if (!action.execute()) {
               return -1;
            }
         } catch (Exception var14) {
            LOGGER.warn((String)"Exception during purge in RollingFileAppender", (Throwable)var14);
            return -1;
         }
      }

      return maxIndex;
   }

   private int purgeDescending(int lowIndex, int highIndex, RollingFileManager manager) {
      int suffixLength = 0;
      List<FileRenameAction> renames = new ArrayList();
      StringBuilder buf = new StringBuilder();
      manager.getPatternProcessor().formatFileName(this.subst, buf, lowIndex);
      String lowFilename = this.subst.replace(buf);
      if (lowFilename.endsWith(".gz")) {
         suffixLength = ".gz".length();
      } else if (lowFilename.endsWith(".zip")) {
         suffixLength = ".zip".length();
      }

      int i;
      for(i = lowIndex; i <= highIndex; ++i) {
         File toRename = new File(lowFilename);
         boolean isBase = false;
         if (suffixLength > 0) {
            File toRenameBase = new File(lowFilename.substring(0, lowFilename.length() - suffixLength));
            if (toRename.exists()) {
               if (toRenameBase.exists()) {
                  LOGGER.debug("DefaultRolloverStrategy.purgeDescending deleting {} base of {}.", toRenameBase, toRename);
                  toRenameBase.delete();
               }
            } else {
               toRename = toRenameBase;
               isBase = true;
            }
         }

         if (!toRename.exists()) {
            break;
         }

         if (i == highIndex) {
            LOGGER.debug("DefaultRolloverStrategy.purgeDescending deleting {} at high index {}: all slots full.", toRename, i);
            if (!toRename.delete()) {
               return -1;
            }
            break;
         }

         buf.setLength(0);
         manager.getPatternProcessor().formatFileName(this.subst, buf, i + 1);
         String highFilename = this.subst.replace(buf);
         String renameTo = highFilename;
         if (isBase) {
            renameTo = highFilename.substring(0, highFilename.length() - suffixLength);
         }

         renames.add(new FileRenameAction(toRename, new File(renameTo), true));
         lowFilename = highFilename;
      }

      for(i = renames.size() - 1; i >= 0; --i) {
         Action action = (Action)renames.get(i);

         try {
            LOGGER.debug("DefaultRolloverStrategy.purgeDescending executing {} of {}: {}", i, renames.size(), action);
            if (!action.execute()) {
               return -1;
            }
         } catch (Exception var13) {
            LOGGER.warn((String)"Exception during purge in RollingFileAppender", (Throwable)var13);
            return -1;
         }
      }

      return lowIndex;
   }

   public RolloverDescription rollover(RollingFileManager manager) throws SecurityException {
      if (this.maxIndex < 0) {
         return null;
      } else {
         long start = System.nanoTime();
         int fileIndex = this.purge(this.minIndex, this.maxIndex, manager);
         if (fileIndex < 0) {
            return null;
         } else {
            if (LOGGER.isTraceEnabled()) {
               double duration = (double)(System.nanoTime() - start) / 1.0E9D;
               LOGGER.trace("DefaultRolloverStrategy.purge() took {} seconds", duration);
            }

            StringBuilder buf = new StringBuilder(255);
            manager.getPatternProcessor().formatFileName(this.subst, buf, fileIndex);
            String currentFileName = manager.getFileName();
            String renameTo = buf.toString();
            String compressedName = renameTo;
            Action compressAction = null;
            if (renameTo.endsWith(".gz")) {
               renameTo = renameTo.substring(0, renameTo.length() - ".gz".length());
               compressAction = new GzCompressAction(new File(renameTo), new File(compressedName), true);
            } else if (renameTo.endsWith(".zip")) {
               renameTo = renameTo.substring(0, renameTo.length() - ".zip".length());
               compressAction = new ZipCompressAction(new File(renameTo), new File(compressedName), true, this.compressionLevel);
            }

            FileRenameAction renameAction = new FileRenameAction(new File(currentFileName), new File(renameTo), false);
            return new RolloverDescriptionImpl(currentFileName, false, renameAction, (Action)compressAction);
         }
      }
   }

   public String toString() {
      return "DefaultRolloverStrategy(min=" + this.minIndex + ", max=" + this.maxIndex + ')';
   }
}
