package basics.profiler;

import java.awt.Toolkit;
import java.util.Iterator;
import java.util.Vector;
import basics.utl.SysUtl;

public class MemoryWatcher
{
private MemoryWatcher()
{
}

private static final MemoryWatcher  watcher            = new MemoryWatcher();
MemoryInfo                          mi                 = null;
boolean                             on                 = false;
Thread                              thread             = null;
static final long                   TOLERANCE          = 5;
static final long                   SHOWERVERY         = 2;
static final long                   INITIALSLEEPTIME   = 1500;
static final long                   MINIMALSLEEPTIME   = 1500;
static final long                   MAXSLEEPTIME       = 1500;
static final long                   INCREMENTSLEEPTIME = 0;
static long                         sleeptime          = INITIALSLEEPTIME;
static Vector<InspectAlertListener> alertListeners     = new Vector<InspectAlertListener>();
static Vector<MemoryListener>       memoryListeners    = new Vector<MemoryListener>();
boolean                             alerted            = false;
static long                         maxkb              = 0;
static long                         counter            = 0;

public static synchronized void exit()
{
   if(watcher.mi == null)
      return;
   watcher.on = false;
   try
   {
      watcher.thread.join();
   }
   catch(InterruptedException e)
   {
   }
   watcher.mi = null;
}

public static synchronized void addListener(InspectAlertListener listener)
{
   if(listener != null)
      alertListeners.add(listener);
}

public static synchronized void addListener(MemoryListener listener)
{
   if(listener != null)
      memoryListeners.add(listener);
}

public static synchronized void setLimitInKb(long alertIfOverKb)
{
   maxkb = alertIfOverKb;
}

private void alert(long limit)
{
   final String msg = "MEMORY: MORE THAN " + limit + " KB IN USE!";
   if(alertListeners.size() == 0)
   {
      SysUtl.println(msg);
      Toolkit.getDefaultToolkit().beep();
   }
   else
      for(Iterator<InspectAlertListener> iter = alertListeners.iterator(); iter.hasNext();)
         iter.next().memoryAlert(msg);
   watcher.alerted = true;
}

static private void notifyChange(String msg)
{
   for(Iterator<MemoryListener> iter = memoryListeners.iterator(); iter.hasNext();)
      iter.next().memoryEvent(msg);
   watcher.alerted = true;
}

public static synchronized void start()
{
   if(watcher.mi != null)
      return;
   watcher.mi = new MemoryInfo();
   watcher.on = true;
   Runnable m = new Runnable()
   {
      long kbUsedOld = 0L;
      int  unchanged = 0;

      public void run()
      {
         while(watcher.on)
         {
            long kbUsed = watcher.mi.getCurrentKBUsed();
            if(Math.abs(kbUsedOld - kbUsed) > TOLERANCE)
            {
               if(!watcher.alerted && maxkb > 0 && kbUsed > maxkb)
                  watcher.alert(maxkb);
               unchanged = 0;
               kbUsedOld = kbUsed;
               sleeptime = watcher.alerted ? MINIMALSLEEPTIME : INITIALSLEEPTIME;
               if(counter % SHOWERVERY == 0)
               {
                  if(memoryListeners.size() == 0)
                     SysUtl.println(watcher.mi.toString());
                  else
                     notifyChange(watcher.mi.toString());
               }
               counter++;
            }
            else
            {
               if(++unchanged % 3 == 0 && sleeptime < MAXSLEEPTIME)
               {
                  sleeptime += INCREMENTSLEEPTIME;
                  if(sleeptime == MAXSLEEPTIME)
                     watcher.alerted = false;
               }
            }
            try
            {
               Thread.sleep(sleeptime);
            }
            catch(InterruptedException e)
            {
            }
         }
      }
   };
   watcher.thread = new Thread(m);
   watcher.thread.setDaemon(true);
   watcher.thread.start();
}
}
