package org.apache.logging.log4j.core.impl;

import java.io.Serializable;
import java.net.URL;
import java.security.CodeSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.Throwables;
import org.apache.logging.log4j.status.StatusLogger;

public class ThrowableProxy implements Serializable {
   private static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0];
   private static final char EOL = '\n';
   private static final Logger LOGGER = StatusLogger.getLogger();
   private static final ThrowableProxy.PrivateSecurityManager SECURITY_MANAGER;
   private static final long serialVersionUID = -2752771578252251910L;
   private final ThrowableProxy causeProxy;
   private int commonElementCount;
   private final ExtendedStackTraceElement[] extendedStackTrace;
   private final String localizedMessage;
   private final String message;
   private final String name;
   private final ThrowableProxy[] suppressedProxies;
   private final transient Throwable throwable;

   private ThrowableProxy() {
      this.throwable = null;
      this.name = null;
      this.extendedStackTrace = null;
      this.causeProxy = null;
      this.message = null;
      this.localizedMessage = null;
      this.suppressedProxies = EMPTY_THROWABLE_PROXY_ARRAY;
   }

   public ThrowableProxy(Throwable throwable) {
      this.throwable = throwable;
      this.name = throwable.getClass().getName();
      this.message = throwable.getMessage();
      this.localizedMessage = throwable.getLocalizedMessage();
      Map<String, ThrowableProxy.CacheEntry> map = new HashMap();
      Stack<Class<?>> stack = this.getCurrentStack();
      this.extendedStackTrace = this.toExtendedStackTrace(stack, map, (StackTraceElement[])null, throwable.getStackTrace());
      Throwable throwableCause = throwable.getCause();
      this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause);
      this.suppressedProxies = this.toSuppressedProxies(throwable);
   }

   private ThrowableProxy(Throwable parent, Stack<Class<?>> stack, Map<String, ThrowableProxy.CacheEntry> map, Throwable cause) {
      this.throwable = cause;
      this.name = cause.getClass().getName();
      this.message = this.throwable.getMessage();
      this.localizedMessage = this.throwable.getLocalizedMessage();
      this.extendedStackTrace = this.toExtendedStackTrace(stack, map, parent.getStackTrace(), cause.getStackTrace());
      this.causeProxy = cause.getCause() == null ? null : new ThrowableProxy(parent, stack, map, cause.getCause());
      this.suppressedProxies = this.toSuppressedProxies(cause);
   }

   public boolean equals(Object obj) {
      if (this == obj) {
         return true;
      } else if (obj == null) {
         return false;
      } else if (this.getClass() != obj.getClass()) {
         return false;
      } else {
         ThrowableProxy other = (ThrowableProxy)obj;
         if (this.causeProxy == null) {
            if (other.causeProxy != null) {
               return false;
            }
         } else if (!this.causeProxy.equals(other.causeProxy)) {
            return false;
         }

         if (this.commonElementCount != other.commonElementCount) {
            return false;
         } else {
            if (this.name == null) {
               if (other.name != null) {
                  return false;
               }
            } else if (!this.name.equals(other.name)) {
               return false;
            }

            if (!Arrays.equals(this.extendedStackTrace, other.extendedStackTrace)) {
               return false;
            } else {
               return Arrays.equals(this.suppressedProxies, other.suppressedProxies);
            }
         }
      }
   }

   private void formatCause(StringBuilder sb, ThrowableProxy cause, List<String> ignorePackages) {
      sb.append("Caused by: ").append(cause).append('\n');
      this.formatElements(sb, cause.commonElementCount, cause.getThrowable().getStackTrace(), cause.extendedStackTrace, ignorePackages);
      if (cause.getCauseProxy() != null) {
         this.formatCause(sb, cause.causeProxy, ignorePackages);
      }

   }

   private void formatElements(StringBuilder sb, int commonCount, StackTraceElement[] causedTrace, ExtendedStackTraceElement[] extStackTrace, List<String> ignorePackages) {
      int count;
      if (ignorePackages != null && !ignorePackages.isEmpty()) {
         count = 0;

         for(int i = 0; i < extStackTrace.length; ++i) {
            if (!this.ignoreElement(causedTrace[i], ignorePackages)) {
               if (count > 0) {
                  if (count == 1) {
                     sb.append("\t....\n");
                  } else {
                     sb.append("\t... suppressed ").append(count).append(" lines\n");
                  }

                  count = 0;
               }

               this.formatEntry(causedTrace[i], extStackTrace[i], sb);
            } else {
               ++count;
            }
         }

         if (count > 0) {
            if (count == 1) {
               sb.append("\t...\n");
            } else {
               sb.append("\t... suppressed ").append(count).append(" lines\n");
            }
         }
      } else {
         for(count = 0; count < extStackTrace.length; ++count) {
            this.formatEntry(causedTrace[count], extStackTrace[count], sb);
         }
      }

      if (commonCount != 0) {
         sb.append("\t... ").append(commonCount).append(" more").append('\n');
      }

   }

   private void formatEntry(StackTraceElement element, ExtendedStackTraceElement extStackTraceElement, StringBuilder sb) {
      sb.append("\tat ");
      sb.append(extStackTraceElement);
      sb.append('\n');
   }

   public void formatWrapper(StringBuilder sb, ThrowableProxy cause) {
      this.formatWrapper(sb, cause, (List)null);
   }

   public void formatWrapper(StringBuilder sb, ThrowableProxy cause, List<String> packages) {
      Throwable caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null;
      if (caused != null) {
         this.formatWrapper(sb, cause.causeProxy);
         sb.append("Wrapped by: ");
      }

      sb.append(cause).append('\n');
      this.formatElements(sb, cause.commonElementCount, cause.getThrowable().getStackTrace(), cause.extendedStackTrace, packages);
   }

   public ThrowableProxy getCauseProxy() {
      return this.causeProxy;
   }

   public String getCauseStackTraceAsString() {
      return this.getCauseStackTraceAsString((List)null);
   }

   public String getCauseStackTraceAsString(List<String> packages) {
      StringBuilder sb = new StringBuilder();
      if (this.causeProxy != null) {
         this.formatWrapper(sb, this.causeProxy);
         sb.append("Wrapped by: ");
      }

      sb.append(this.toString());
      sb.append('\n');
      this.formatElements(sb, 0, this.throwable.getStackTrace(), this.extendedStackTrace, packages);
      return sb.toString();
   }

   public int getCommonElementCount() {
      return this.commonElementCount;
   }

   private Stack<Class<?>> getCurrentStack() {
      if (ReflectiveCallerClassUtility.isSupported()) {
         Stack<Class<?>> classes = new Stack();
         int index = 1;

         for(Class clazz = ReflectiveCallerClassUtility.getCaller(index); clazz != null; clazz = ReflectiveCallerClassUtility.getCaller(index)) {
            classes.push(clazz);
            ++index;
         }

         return classes;
      } else if (SECURITY_MANAGER == null) {
         return new Stack();
      } else {
         Class<?>[] array = SECURITY_MANAGER.getClasses();
         Stack<Class<?>> classes = new Stack();
         Class[] arr$ = array;
         int len$ = array.length;

         for(int i$ = 0; i$ < len$; ++i$) {
            Class<?> clazz = arr$[i$];
            classes.push(clazz);
         }

         return classes;
      }
   }

   public ExtendedStackTraceElement[] getExtendedStackTrace() {
      return this.extendedStackTrace;
   }

   public String getExtendedStackTraceAsString() {
      return this.getExtendedStackTraceAsString((List)null);
   }

   public String getExtendedStackTraceAsString(List<String> ignorePackages) {
      StringBuilder sb = new StringBuilder(this.name);
      String msg = this.message;
      if (msg != null) {
         sb.append(": ").append(msg);
      }

      sb.append('\n');
      this.formatElements(sb, 0, this.throwable.getStackTrace(), this.extendedStackTrace, ignorePackages);
      if (this.causeProxy != null) {
         this.formatCause(sb, this.causeProxy, ignorePackages);
      }

      return sb.toString();
   }

   public String getLocalizedMessage() {
      return this.localizedMessage;
   }

   public String getMessage() {
      return this.message;
   }

   public String getName() {
      return this.name;
   }

   public StackTraceElement[] getStackTrace() {
      return this.throwable == null ? null : this.throwable.getStackTrace();
   }

   public ThrowableProxy[] getSuppressedProxies() {
      return this.suppressedProxies;
   }

   public String getSuppressedStackTrace() {
      ThrowableProxy[] suppressed = this.getSuppressedProxies();
      if (suppressed != null && suppressed.length != 0) {
         StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:\n");
         ThrowableProxy[] arr$ = suppressed;
         int len$ = suppressed.length;

         for(int i$ = 0; i$ < len$; ++i$) {
            ThrowableProxy proxy = arr$[i$];
            sb.append(proxy.getExtendedStackTraceAsString());
         }

         return sb.toString();
      } else {
         return "";
      }
   }

   public Throwable getThrowable() {
      return this.throwable;
   }

   public int hashCode() {
      int prime = true;
      int result = 1;
      int result = 31 * result + (this.causeProxy == null ? 0 : this.causeProxy.hashCode());
      result = 31 * result + this.commonElementCount;
      result = 31 * result + (this.extendedStackTrace == null ? 0 : Arrays.hashCode(this.extendedStackTrace));
      result = 31 * result + (this.suppressedProxies == null ? 0 : Arrays.hashCode(this.suppressedProxies));
      result = 31 * result + (this.name == null ? 0 : this.name.hashCode());
      return result;
   }

   private boolean ignoreElement(StackTraceElement element, List<String> ignorePackages) {
      String className = element.getClassName();
      Iterator i$ = ignorePackages.iterator();

      String pkg;
      do {
         if (!i$.hasNext()) {
            return false;
         }

         pkg = (String)i$.next();
      } while(!className.startsWith(pkg));

      return true;
   }

   private Class<?> loadClass(ClassLoader lastLoader, String className) {
      Class clazz;
      if (lastLoader != null) {
         try {
            clazz = Loader.initializeClass(className, lastLoader);
            if (clazz != null) {
               return clazz;
            }
         } catch (Exception var8) {
         }
      }

      try {
         clazz = Loader.loadClass(className);
      } catch (ClassNotFoundException var7) {
         try {
            clazz = Loader.initializeClass(className, this.getClass().getClassLoader());
         } catch (ClassNotFoundException var6) {
            return null;
         }
      }

      return clazz;
   }

   private ThrowableProxy.CacheEntry toCacheEntry(StackTraceElement stackTraceElement, Class<?> callerClass, boolean exact) {
      String location = "?";
      String version = "?";
      ClassLoader lastLoader = null;
      if (callerClass != null) {
         try {
            CodeSource source = callerClass.getProtectionDomain().getCodeSource();
            if (source != null) {
               URL locationURL = source.getLocation();
               if (locationURL != null) {
                  String str = locationURL.toString().replace('\\', '/');
                  int index = str.lastIndexOf("/");
                  if (index >= 0 && index == str.length() - 1) {
                     index = str.lastIndexOf("/", index - 1);
                     location = str.substring(index + 1);
                  } else {
                     location = str.substring(index + 1);
                  }
               }
            }
         } catch (Exception var11) {
         }

         Package pkg = callerClass.getPackage();
         if (pkg != null) {
            String ver = pkg.getImplementationVersion();
            if (ver != null) {
               version = ver;
            }
         }

         lastLoader = callerClass.getClassLoader();
      }

      return new ThrowableProxy.CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader);
   }

   ExtendedStackTraceElement[] toExtendedStackTrace(Stack<Class<?>> stack, Map<String, ThrowableProxy.CacheEntry> map, StackTraceElement[] rootTrace, StackTraceElement[] stackTrace) {
      int stackLength;
      if (rootTrace != null) {
         int rootIndex = rootTrace.length - 1;

         int stackIndex;
         for(stackIndex = stackTrace.length - 1; rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex]); --stackIndex) {
            --rootIndex;
         }

         this.commonElementCount = stackTrace.length - 1 - stackIndex;
         stackLength = stackIndex + 1;
      } else {
         this.commonElementCount = 0;
         stackLength = stackTrace.length;
      }

      ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength];
      Class<?> clazz = stack.isEmpty() ? null : (Class)stack.peek();
      ClassLoader lastLoader = null;

      for(int i = stackLength - 1; i >= 0; --i) {
         StackTraceElement stackTraceElement = stackTrace[i];
         String className = stackTraceElement.getClassName();
         ExtendedClassInfo extClassInfo;
         ThrowableProxy.CacheEntry entry;
         if (clazz != null && className.equals(clazz.getName())) {
            entry = this.toCacheEntry(stackTraceElement, clazz, true);
            extClassInfo = entry.element;
            lastLoader = entry.loader;
            stack.pop();
            clazz = stack.isEmpty() ? null : (Class)stack.peek();
         } else if (map.containsKey(className)) {
            entry = (ThrowableProxy.CacheEntry)map.get(className);
            extClassInfo = entry.element;
            if (entry.loader != null) {
               lastLoader = entry.loader;
            }
         } else {
            entry = this.toCacheEntry(stackTraceElement, this.loadClass(lastLoader, className), false);
            extClassInfo = entry.element;
            map.put(stackTraceElement.toString(), entry);
            if (entry.loader != null) {
               lastLoader = entry.loader;
            }
         }

         extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo);
      }

      return extStackTrace;
   }

   public String toString() {
      String msg = this.message;
      return msg != null ? this.name + ": " + msg : this.name;
   }

   private ThrowableProxy[] toSuppressedProxies(Throwable thrown) {
      try {
         Throwable[] suppressed = Throwables.getSuppressed(thrown);
         if (suppressed == null) {
            return EMPTY_THROWABLE_PROXY_ARRAY;
         } else {
            ThrowableProxy[] proxies = new ThrowableProxy[suppressed.length];

            for(int i = 0; i < suppressed.length; ++i) {
               proxies[i] = new ThrowableProxy(suppressed[i]);
            }

            return proxies;
         }
      } catch (Exception var5) {
         StatusLogger.getLogger().error(var5);
         return null;
      }
   }

   static {
      if (ReflectiveCallerClassUtility.isSupported()) {
         SECURITY_MANAGER = null;
      } else {
         ThrowableProxy.PrivateSecurityManager securityManager;
         try {
            securityManager = new ThrowableProxy.PrivateSecurityManager();
            if (securityManager.getClasses() == null) {
               securityManager = null;
               LOGGER.error("Unable to obtain call stack from security manager.");
            }
         } catch (Exception var2) {
            securityManager = null;
            LOGGER.debug((String)"Unable to install security manager.", (Throwable)var2);
         }

         SECURITY_MANAGER = securityManager;
      }

   }

   private static class PrivateSecurityManager extends SecurityManager {
      private PrivateSecurityManager() {
      }

      public Class<?>[] getClasses() {
         return this.getClassContext();
      }

      // $FF: synthetic method
      PrivateSecurityManager(Object x0) {
         this();
      }
   }

   static class CacheEntry {
      private final ExtendedClassInfo element;
      private final ClassLoader loader;

      public CacheEntry(ExtendedClassInfo element, ClassLoader loader) {
         this.element = element;
         this.loader = loader;
      }
   }
}
