package org.apache.logging.log4j.status;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.simple.SimpleLogger;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.util.PropertiesUtil;

public final class StatusLogger extends AbstractLogger {
   private static final long serialVersionUID = 2L;
   public static final String MAX_STATUS_ENTRIES = "log4j2.status.entries";
   private static final String NOT_AVAIL = "?";
   private static final PropertiesUtil PROPS = new PropertiesUtil("log4j2.StatusLogger.properties");
   private static final int MAX_ENTRIES;
   private static final String DEFAULT_STATUS_LEVEL;
   private static final StatusLogger STATUS_LOGGER;
   private final SimpleLogger logger;
   private final Collection<StatusListener> listeners = new CopyOnWriteArrayList();
   private final ReadWriteLock listenersLock = new ReentrantReadWriteLock();
   private final Queue<StatusData> messages;
   private final Lock msgLock;
   private int listenersLevel;

   private StatusLogger() {
      this.messages = new StatusLogger.BoundedQueue(MAX_ENTRIES);
      this.msgLock = new ReentrantLock();
      this.logger = new SimpleLogger("StatusLogger", Level.ERROR, false, true, false, false, "", (MessageFactory)null, PROPS, System.err);
      this.listenersLevel = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
   }

   public static StatusLogger getLogger() {
      return STATUS_LOGGER;
   }

   public void setLevel(Level level) {
      this.logger.setLevel(level);
   }

   public void registerListener(StatusListener listener) {
      this.listenersLock.writeLock().lock();

      try {
         this.listeners.add(listener);
         Level lvl = listener.getStatusLevel();
         if (this.listenersLevel < lvl.intLevel()) {
            this.listenersLevel = lvl.intLevel();
         }
      } finally {
         this.listenersLock.writeLock().unlock();
      }

   }

   public void removeListener(StatusListener listener) {
      closeSilently(listener);
      this.listenersLock.writeLock().lock();

      try {
         this.listeners.remove(listener);
         int lowest = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
         Iterator i$ = this.listeners.iterator();

         while(i$.hasNext()) {
            StatusListener l = (StatusListener)i$.next();
            int level = l.getStatusLevel().intLevel();
            if (lowest < level) {
               lowest = level;
            }
         }

         this.listenersLevel = lowest;
      } finally {
         this.listenersLock.writeLock().unlock();
      }
   }

   public Iterable<StatusListener> getListeners() {
      return this.listeners;
   }

   public void reset() {
      this.listenersLock.writeLock().lock();

      try {
         Iterator i$ = this.listeners.iterator();

         while(i$.hasNext()) {
            StatusListener listener = (StatusListener)i$.next();
            closeSilently(listener);
         }
      } finally {
         this.listeners.clear();
         this.listenersLock.writeLock().unlock();
         this.clear();
      }

   }

   private static void closeSilently(Closeable resource) {
      try {
         resource.close();
      } catch (IOException var2) {
      }

   }

   public List<StatusData> getStatusData() {
      this.msgLock.lock();

      ArrayList var1;
      try {
         var1 = new ArrayList(this.messages);
      } finally {
         this.msgLock.unlock();
      }

      return var1;
   }

   public void clear() {
      this.msgLock.lock();

      try {
         this.messages.clear();
      } finally {
         this.msgLock.unlock();
      }

   }

   public Level getLevel() {
      return this.logger.getLevel();
   }

   public void logMessage(String fqcn, Level level, Marker marker, Message msg, Throwable t) {
      StackTraceElement element = null;
      if (fqcn != null) {
         element = this.getStackTraceElement(fqcn, Thread.currentThread().getStackTrace());
      }

      StatusData data = new StatusData(element, level, msg, t);
      this.msgLock.lock();

      try {
         this.messages.add(data);
      } finally {
         this.msgLock.unlock();
      }

      if (this.listeners.size() > 0) {
         Iterator i$ = this.listeners.iterator();

         while(i$.hasNext()) {
            StatusListener listener = (StatusListener)i$.next();
            if (data.getLevel().isMoreSpecificThan(listener.getStatusLevel())) {
               listener.log(data);
            }
         }
      } else {
         this.logger.logMessage(fqcn, level, marker, msg, t);
      }

   }

   private StackTraceElement getStackTraceElement(String fqcn, StackTraceElement[] stackTrace) {
      if (fqcn == null) {
         return null;
      } else {
         boolean next = false;
         StackTraceElement[] arr$ = stackTrace;
         int len$ = stackTrace.length;

         for(int i$ = 0; i$ < len$; ++i$) {
            StackTraceElement element = arr$[i$];
            String className = element.getClassName();
            if (next && !fqcn.equals(className)) {
               return element;
            }

            if (fqcn.equals(className)) {
               next = true;
            } else if ("?".equals(className)) {
               break;
            }
         }

         return null;
      }
   }

   public boolean isEnabled(Level level, Marker marker, String message, Throwable t) {
      return this.isEnabled(level, marker);
   }

   public boolean isEnabled(Level level, Marker marker, String message) {
      return this.isEnabled(level, marker);
   }

   public boolean isEnabled(Level level, Marker marker, String message, Object... params) {
      return this.isEnabled(level, marker);
   }

   public boolean isEnabled(Level level, Marker marker, Object message, Throwable t) {
      return this.isEnabled(level, marker);
   }

   public boolean isEnabled(Level level, Marker marker, Message message, Throwable t) {
      return this.isEnabled(level, marker);
   }

   public boolean isEnabled(Level level, Marker marker) {
      if (this.listeners.size() > 0) {
         return this.listenersLevel >= level.intLevel();
      } else {
         return this.logger.isEnabled(level, marker);
      }
   }

   static {
      MAX_ENTRIES = PROPS.getIntegerProperty("log4j2.status.entries", 200);
      DEFAULT_STATUS_LEVEL = PROPS.getStringProperty("log4j2.StatusLogger.level");
      STATUS_LOGGER = new StatusLogger();
   }

   private class BoundedQueue<E> extends ConcurrentLinkedQueue<E> {
      private static final long serialVersionUID = -3945953719763255337L;
      private final int size;

      public BoundedQueue(int size) {
         this.size = size;
      }

      public boolean add(E object) {
         while(StatusLogger.this.messages.size() > this.size) {
            StatusLogger.this.messages.poll();
         }

         return super.add(object);
      }
   }
}
