/**
 * 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.activemq.bugs.embedded;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

public class ThreadExplorer {

   static Logger logger = Logger.getLogger(ThreadExplorer.class);

   public static Thread[] listThreads() {

      int nThreads = Thread.activeCount();
      Thread ret[] = new Thread[nThreads];

      Thread.enumerate(ret);

      return ret;

   }

   /**
    * Helper function to access a thread per name (ignoring case)
    *
    * @param name
    * @return
    */
   public static Thread fetchThread(String name) {
      Thread[] threadArray = listThreads();
      // for (Thread t : threadArray)
      for (int i = 0; i < threadArray.length; i++) {
         Thread t = threadArray[i];
         if (t.getName().equalsIgnoreCase(name))
            return t;
      }
      return null;
   }

   /**
    * Allow for killing threads
    *
    * @param threadName
    * @param isStarredExp (regular expressions with *)
    */
   @SuppressWarnings("deprecation")
   public static int kill(String threadName, boolean isStarredExp, String motivation) {
      String me = "ThreadExplorer.kill: ";
      if (logger.isDebugEnabled()) {
         logger.debug("Entering " + me + " with " + threadName + " isStarred: " + isStarredExp);
      }
      int ret = 0;
      Pattern mypattern = null;
      if (isStarredExp) {
         String realreg = threadName.toLowerCase().replaceAll("\\*", "\\.\\*");
         mypattern = Pattern.compile(realreg);

      }
      Thread[] threads = listThreads();
      for (int i = 0; i < threads.length; i++) {
         Thread thread = threads[i];
         if (thread == null)
            continue;
         // kill the thread unless it is not current thread
         boolean matches = false;

         if (isStarredExp) {
            Matcher matcher = mypattern.matcher(thread.getName().toLowerCase());
            matches = matcher.matches();
         }
         else {
            matches = (thread.getName().equalsIgnoreCase(threadName));
         }
         if (matches && (Thread.currentThread() != thread) && !thread.getName().equals("main")) {
            if (logger.isInfoEnabled())
               logger.info("Killing thread named [" + thread.getName() + "]"); // , removing its uncaught
            // exception handler to
            // avoid ThreadDeath
            // exception tracing
            // "+motivation );

            ret++;

            // PK leaving uncaught exception handler otherwise master push
            // cannot recover from this error
            // thread.setUncaughtExceptionHandler(null);
            try {
               thread.stop();
            }
            catch (ThreadDeath e) {
               logger.warn("Thread already death.", e);
            }

         }
      }
      return ret;
   }

   public static String show(String title) {
      StringBuffer out = new StringBuffer();
      Thread[] threadArray = ThreadExplorer.listThreads();

      out.append(title + "\n");
      for (int i = 0; i < threadArray.length; i++) {
         Thread thread = threadArray[i];

         if (thread != null) {
            out.append("* [" + thread.getName() + "] " + (thread.isDaemon() ? "(Daemon)" : "") + " Group: " + thread.getThreadGroup().getName() + "\n");
         }
         else {
            out.append("* ThreadDeath: " + thread + "\n");
         }

      }
      return out.toString();
   }

   public static int active() {
      int count = 0;
      Thread[] threadArray = ThreadExplorer.listThreads();

      for (int i = 0; i < threadArray.length; i++) {
         Thread thread = threadArray[i];
         if (thread != null) {
            count++;
         }
      }

      return count;
   }

}
