// ==========================================================================
// $Id: ActionList.java,v 1.2 2002/09/09 20:12:29 hitoshi Exp $
// Time Action List
// ==========================================================================
// Copyright (C) 2002 Yamauchi, Hitoshi
//
//   Max-Planck-Institut fuer Informatik AG4
//   Stuhlsatzenhausweg 85, 66123 Saarbruecken
//   http://www.mpi-sb.mpg.de/
// 
// This software can be redistributed only under GNU copyleft.
//
// @author Yamauchi Hitoshi 山内 斉
// ==========================================================================
// $Revision: 1.2 $
// $Date: 2002/09/09 20:12:29 $
// $Author: hitoshi $
// ==========================================================================
package de.mpg.mpisb.timerclock;

import  java.util.*;

/** 
 * Action List 
 *
 *   Action List : the design of this class has the same concept as
 *   discrete time simulation, namely time wheel. But for the
 *   implementation simplicity, class Actionable (usually this is
 *   called ad `event'in the discrete time simulation) has always time
 *   to fire the action. It is usually not needed, since such time can
 *   be shared among at-the-same-time-events.
 *
 *   There are two lists, one contains all actions with sorted by
 *   elapsed time. The other is just copy for processing. Original
 *   actions are kept since restoring the actions when timer is
 *   reseted.
 * 
 *   A sample action implementation is AktionBeep.java. @see AktionBeep
 *
 *   このクラスの設計のコンセプトは離散時間シミュレーションで利用される
 *   通常の手法，Time Wheel 法と同じ．リストが2つあるのは，タイマーがリ
 *   セットされた場合に戻すため．
 * 
 */
public class ActionList
{
  /**
   * constructor
   */
  public ActionList()
  {
    this.mActionKeepList = new LinkedList();
    this.mProcList 	 = null;
  }

  /**
   * reset actions : initialize for processing.
   */
  public void resetActionsForProc()
  {
    Message.verboseOut("DEBUG: reset actions\n");
    this.mProcList = new LinkedList();
    for(int i = 0; i < this.mActionKeepList.size(); i++)
    {
      Vector stimeEvs = (Vector)this.mActionKeepList.get(i);
      this.mProcList.add(stimeEvs);

      Message.verboseOut("DEBUG: ");
      for(int j = 0; j < stimeEvs.size(); j++)
      {
	Actionable a = (Actionable)(stimeEvs.get(j));
	Message.verboseOut(" " + a);
      }
      Message.verboseOut("\n");
    }
  }

  /**
   * add action
   *  put the action with sorting according to the actionfiretime.
   */
  public void addAction(Actionable act)
  {
    final int tsize = this.mActionKeepList.size();

    for(int i = 0; i < tsize; i++)
    {
      Vector sameTimeActs = (Vector)(this.mActionKeepList.get(i));
      Actionable a = (Actionable)(sameTimeActs.get(0));

      // in the case of this act should fire before the current one.
      if(act.getActionTime() < a.getActionTime())
      {
	Vector newtimeactions = new Vector();
	newtimeactions.add(act);
	this.mActionKeepList.add(newtimeactions);
	return;	
      }

      // in the case of the same time
      if(a.getActionTime() == act.getActionTime())
      {
	sameTimeActs.add(act);
	return;
      }
    }

    // If the act is not added, put it the last. or when action list
    // was empty, add it to the last.
    Vector newtimeactions = new Vector();
    newtimeactions.add(act);
    this.mActionKeepList.add(newtimeactions);
  }

  /**
   * processing actions
   */
  public void procActions(long elapsedtime)
  {
    assert(this.mProcList != null);

    final int tsize = this.mProcList.size();
    if(tsize == 0)
    {
      return;			// nothing to do
    }

    while(true)
    {
      Vector sameTimeActs = (Vector)(this.mProcList.get(0));
      Actionable a = (Actionable)(sameTimeActs.get(0));

      if(a.getActionTime() <= elapsedtime)
      {
	// Message.verboseOut("DEBUG: Action! et = " + elapsedtime);

	// do all actions which should be acted now.
	for(int j = 0; j < sameTimeActs.size(); j++)
	{
	  Actionable doaction = (Actionable)(sameTimeActs.get(j));
	  doaction.doit();
	  Message.verboseOut("DEBUG: fire action " + doaction + "\n");
	}
	this.mProcList.removeFirst();	
	System.out.println();
	if(this.mProcList.size() == 0)
	{
	  break;		// no actions anymore
	}
      }
      else
      {
	// nothing to do any more
	break;
      }
    }
  }

  /**
   * peek submitted actions
   *   @return submitted actions LinkedList. Since this is reference,
   *   if you change this list, actions of the inside of ActionList
   *   will be changed.
   */
  public LinkedList peekSubmittedActions()
  {
    return(this.mActionKeepList);
  }

  /**
   * toString
   */
  public String toString()
  {
    String s = "ActionKeepList:";
    for(int i = 0; i < this.mActionKeepList.size(); i++)
    {
      Vector sameTimeActs = (Vector)(this.mActionKeepList.get(i));
      Actionable a = (Actionable)(sameTimeActs.get(0));
      s += ("\nelapsed time = "   + a.getActionTime() + 
	    ", # of actions = " + sameTimeActs.size());
    }

    s += ("\nProcList is " + ((this.mProcList == null) ? "not " : "") +
	  "initialized.");
    return(s);
  }
  
  // private variables
  private LinkedList mActionKeepList; // keep actions 
  private LinkedList mProcList; // only for processing

  /*
   * main for test 
   */
  public static void main(String [] args)
  {
    // create ActionList
    ActionList al = new ActionList();

    // add actions
    al.addAction(new AktionBeep(10));
    al.addAction(new AktionBeep(10));
    al.addAction(new AktionBeep(20));

    // init for processing
    al.resetActionsForProc();

    try {
      for(long elapsed_t = 0; elapsed_t < 30; elapsed_t++)
      {
	System.out.println("etime = " + elapsed_t);
	al.procActions(elapsed_t);
	Thread.sleep(200);      
      }

      // reset
      System.out.println("Reset the actions.");
      al.resetActionsForProc();

      for(long elapsed_t = 0; elapsed_t < 30; elapsed_t++)
      {
	System.out.println("etime = " + elapsed_t);
	al.procActions(elapsed_t);
	Thread.sleep(200);      
      }
    } 
    catch (InterruptedException e)
    {
      System.out.println(e);
    }
  }
}
