package at.gv.egovernment.moa.util;

import java.util.Map;
import java.util.WeakHashMap;

/**
 * A timer utility for named timers.
 * 
 * @author Sven Aigner
 */
public class MOATimer {

  /** The single instance of this class. */
  private static MOATimer instance = null;
  /** The starting points of single timings. */
  private static Map timemapstart = new WeakHashMap();
  /** The end points of single timings. */
  private static Map timemapend = new WeakHashMap();

  /**
   * Return the single instance of this class.
   * 
   * @return The single instance of this class.
   */
  public static MOATimer getInstance() {
    if (instance == null) {
      instance = new MOATimer();
    }
    return instance;
  }

  /**
   * Create a new <code>MOATimer</code>.
   * 
   * Protected to disallow multiple instances.
   */
  protected MOATimer() {
    super();
  }

  /**
   * Start timing a certain action.
   * 
   * The timing belonging to the action ID is garbage collected as soon as there
   * exists no other reference to the action ID.
   * 
   * @param id The action ID.
   */
  public void startTiming(Object id) {
    timemapstart.put(id, new Long(System.currentTimeMillis()));
  }

  /**
   * Stop timing an action.
   * 
   * @param id The action ID.
   */
  public void stopTiming(Object id) {
    timemapend.put(id, new Long(System.currentTimeMillis()));
  }

  /**
   * Get the duration of an action.
   * 
   * @param id The action ID for which to compute the duration.
   * @return long The duration in milliseconds between calls to
   * <code>startTiming()</code> and <code>stopTiming()</code>. If
   * only <code>startTiming()</code> has been called for the action, then
   * current difference to the system time is returned. If no timing exists for
   * the action, <code>- 1</code> is returned.
   */
  public long duration(Object id) {
    if (timemapstart.containsKey(id)) {
      long start = ((Long) timemapstart.get(id)).longValue();
      if (timemapend.containsKey(id)) {
        long end = ((Long) timemapend.get(id)).longValue();
        return end - start;
      } else {
        return System.currentTimeMillis() - start;
      }
    } else
      return -1;
  }

  /**
   * Get the duration of an action, as a nicely formatted <code>String</code>.
   * 
   * @param id The action ID.
   * @return String The <code>duration()</code> as a <code>String</code>.
   */
  public String durationAsString(Object id) {
    long dur = duration(id);
    long second = dur / 1000;
    long mil = (dur) - (second * 1000);
    return "Duration: " + second + "." + mil + " seconds";
  }

  /**
   * Remove a timing.
   * 
   * @param id The action ID.
   */
  public void clearTiming(String id) {
    if (timemapstart.containsKey(id))
      timemapstart.remove(id);
    if (timemapend.containsKey(id))
      timemapend.remove(id);
  }

}