package com.airlock.iam.servicecontainer.app.infrastructure.server.threadpool;

import com.airlock.iam.base.misc.concurrent.ThreadUtils;
import com.airlock.iam.common.domain.service.session.IdentityStringGenerator;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.elog.api.LogTypes;
import com.airlock.iam.servicecontainer.api.domain.model.task.StoppableTask;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class ThreadPool {
   static final ELogger LOG = ELoggerFactory.getLogger(ThreadPool.class);
   private final int maxProcessors;
   private int totalNumberOfThreads;
   private final List<WorkerThread> sleepingThreads;
   private final Object listLock = new Object();
   private boolean doNotStartThreads;

   public ThreadPool(int maxProcessors) {
      this.maxProcessors = maxProcessors;
      this.sleepingThreads = new ArrayList<>(this.maxProcessors + 100);
   }

   public void start(int minProcessors) {
      LOG.info(LogTypes.SERVER_INFO_SYS, "Starting " + minProcessors + " threads.");

      for (int i = 0; i < minProcessors; i++) {
         WorkerThread thread = new WorkerThread(this);
         this.addSleepingThread(thread);
      }
   }

   public int getNumberOfSleepingThreads() {
      synchronized (this.listLock) {
         return this.sleepingThreads.size();
      }
   }

   public int getNumberOfWorkingThreads() {
      synchronized (this.listLock) {
         return this.totalNumberOfThreads - this.sleepingThreads.size();
      }
   }

   public int getNumberOfThreads() {
      synchronized (this.listLock) {
         return this.totalNumberOfThreads;
      }
   }

   private void addSleepingThread(WorkerThread t) {
      synchronized (this.listLock) {
         if (LOG.isDebugEnabled()) {
            LOG.debug("Adding thread " + t.getName() + " to set of sleeping threads.");
         }

         this.sleepingThreads.add(t);
         this.totalNumberOfThreads++;
      }
   }

   private WorkerThread getThread(boolean block) {
      WorkerThread wt = null;
      synchronized (this.listLock) {
         if (!this.sleepingThreads.isEmpty()) {
            wt = this.sleepingThreads.removeLast();
            if (LOG.isDebugEnabled()) {
               LOG.debug("got sleeping thread " + wt.getName() + " from thread pool");
            }

            if (wt != null) {
               return wt;
            }
         }
      }

      synchronized (this.listLock) {
         if (this.totalNumberOfThreads < this.maxProcessors && !this.doNotStartThreads) {
            wt = new WorkerThread(this);
            this.totalNumberOfThreads++;
            if (LOG.isDebugEnabled()) {
               LOG.debug("created new thread " + wt.getName() + ". " + this.totalNumberOfThreads + " threads exist now.");
            }
         } else {
            LOG.warn(
               LogTypes.SERVER_INFO_SYS,
               "Could not create new processor thread, because Connector.maxProcessors limit is reached. If this happens frequently, increase this value."
            );
            if (!block) {
               return null;
            }

            while (!this.doNotStartThreads) {
               try {
                  this.listLock.wait();
               } catch (InterruptedException var6) {
               }

               if (!this.sleepingThreads.isEmpty()) {
                  wt = this.sleepingThreads.removeLast();
                  if (LOG.isDebugEnabled()) {
                     LOG.debug("got sleeping thread " + wt.getName() + " from thread pool");
                  }

                  if (wt != null) {
                     return wt;
                  }
               }
            }
         }

         return wt;
      }
   }

   @SuppressFBWarnings("NO_NOTIFY_NOT_NOTIFYALL")
   void putBackInSleepersList(WorkerThread wt) {
      synchronized (this.listLock) {
         this.sleepingThreads.add(wt);
         if (LOG.isDebugEnabled()) {
            LOG.debug("putting thread " + wt.getName() + " back to sleepers list");
         }

         this.listLock.notify();
      }
   }

   public boolean executeTask(StoppableTask worker, boolean block, String taskName) {
      WorkerThread wt = this.getThread(block);
      if (null == wt) {
         return false;
      }

      wt.setTaskInfo(worker, taskName, IdentityStringGenerator.generateIdentityString(), IdentityStringGenerator.generateIdentityString());
      if (LOG.isDebugEnabled()) {
         LOG.debug("execute: got thread " + wt.getName() + " to handle worker " + worker.getThreadName());
      }

      synchronized (wt) {
         if (wt.isAlive()) {
            wt.wakeup();
         } else {
            wt.start();
         }

         return true;
      }
   }

   public void stopAllThreads() {
      LOG.info("Stopping all threads of thread-pool ...");
      int threadsLeft;
      synchronized (this.listLock) {
         this.doNotStartThreads = true;
         threadsLeft = this.totalNumberOfThreads;
      }

      while (threadsLeft > 0) {
         synchronized (this.listLock) {
            while (!this.sleepingThreads.isEmpty()) {
               WorkerThread wt = this.sleepingThreads.removeLast();
               if (LOG.isDebugEnabled()) {
                  LOG.debug("stopping thread " + wt.getName());
               }

               wt.setStopped();
               this.totalNumberOfThreads--;
            }

            threadsLeft = this.totalNumberOfThreads;
         }

         if (threadsLeft > 0) {
            ThreadUtils.sleepInterruptibly(100L, TimeUnit.MILLISECONDS);
         }
      }
   }

   void threadDies() {
      synchronized (this.listLock) {
         this.totalNumberOfThreads--;
      }
   }

   @SuppressFBWarnings("NO_NOTIFY_NOT_NOTIFYALL")
   void handleUnexpectedThreadDeath() {
      synchronized (this.listLock) {
         WorkerThread wt = new WorkerThread(this);
         this.sleepingThreads.add(wt);
         this.listLock.notify();
      }

      LOG.info(LogTypes.SERVER_INFO_SYS, "Created new thread because a thread died unexpectedly");
   }
}
