/////////////////////////////////////////////////////////////////////////////
// waitunti.cc
//
// SIMLIB version: 2.18
// Date: 2004-01-25
//
// Copyright (c) 1991-2004 Petr Peringer 
//
// This library is licensed under GNU Library GPL. See the file COPYING.
//

//
//  class WaitUntilList --- not good implementation
//  (uses list of waiting processes, and checks conditions after each
//  activated event)
//  better implementation will use objects in WUexpressions
//
// 199808  updated:  uses standard list<>

////////////////////////////////////////////////////////////////////////////
// interface
//
#include "simlib.h"
#include "internal.h"


////////////////////////////////////////////////////////////////////////////
// implementation
//

SIMLIB_IMPLEMENTATION

//==========================================================================
#ifdef NEW_WULIST

#include <list>

////////////////////////////////////////////////////////////////////////////
// class WaitUntilList --- singleton
//
class WaitUntilList {
    typedef std::list<Process *> container_t;
    container_t l;
    static WaitUntilList *instance;   // unique list
  public:
    typedef container_t::iterator iterator;
    static iterator begin() { return instance->l.begin(); }
    static iterator end() { return instance->l.end(); }
    static bool empty() { return instance->l.empty(); }
    static void InsertCurrent();     // insert current process into list
    static void GetCurrent();        // get current process
    static void WU_hook(); // active: next process in WUlist or 0
    static void Remove(Process *p) { // find and remove p
        dprintf(("WaitUntil::Remove(%s)", p->Name()));
        instance->l.remove(p); // should be in list 
    }
    static void clear();    // empty
    static void create() {  // create single instance
        if(instance==0) instance = new WaitUntilList; 
	else            SIMLIB_internal_error(); // called twice
	INSTALL_HOOK(WUclear, WaitUntilList::clear);
        SIMLIB_atexit(destroy); // last SIMLIB module cleanup calls this
    }
    static void destroy() {  // destroy single instance
        clear();             // remove all contents
	delete instance;
	instance = 0;
    }
  private:
    WaitUntilList() { dprintf(("WaitUntilList::WaitUntilList()")); }
    ~WaitUntilList() { dprintf(("WaitUntilList::~WaitUntilList()")); }
    // destructor never called ###???
    static iterator current;    
#ifndef NDEBUG
    friend void WU_print();
#endif
};

#ifndef NDEBUG
    void WU_print() {
       _Print("WaitUntilList:\n");
       if(WaitUntilList::instance == 0) { _Print("none\n"); return; }
       WaitUntilList::iterator i = WaitUntilList::begin();
       for( int n=0 ; i!=WaitUntilList::end() ; ++i, ++n )
	 _Print(" [%d] %s\n", n, (*i)->Name() );
    }
#endif

// WaitUntilList single instance
WaitUntilList *WaitUntilList::instance = 0; // static
WaitUntilList::iterator WaitUntilList::current; // static

////////////////////////////////////////////////////////////////////////////
static bool flag = false; // valid iterator in WUList
////////////////////////////////////////////////////////////////////////////
// main WUlist interface function
void WaitUntilList::WU_hook() { // get ptr to next process in WUlist or 0
    dprintf(("WaitUntilList::WU_hook"));
    //  PRECONDITION: WUList not empty
    if(WaitUntilList::empty()) // this should never happen
    	SIMLIB_internal_error();

    if(!flag) { // start processing, (first call or after remove)
        current = WaitUntilList::begin(); // reset to first process 
	flag = true;
	// loop_count++;
	// if(loop_count>LIMIT) error("waituntil-loop");
	SIMLIB_Current = *current;  // always OK
	return; 
    } 
    ++current;  // next waiting process
    if( current != WaitUntilList::end() ) { // not end
        SIMLIB_Current = *current;  
        return;  
    }
    flag = false; // no next process --- end of WaitUntil processing 
    // loop_count = 0;
    SIMLIB_Current = 0; // not needed ???### 
    return; 
}

////////////////////////////////////////////////////////////////////////////
// Process::
////////////////////////////////////////////////////////////////////////////
// _WaitUntil --- wait to condition
// this is hidden by macro WaitUntil(b), useable in Process::Behavior
//
bool Process::_WaitUntil(bool test)
{
  dprintf(("%s._WaitUntil(%s)", Name(), test?"true":"false" ));
  if(test) {                    // true --- end of wait
    WaitUntilList::GetCurrent(); // ***** remove from WUList
    _wait_until = false;        // not in WUlist
    return false;               // continue checking WaitUntil condition
  } else {                      // false --- wait
    if (SIMLIB_Current != this) SIMLIB_internal_error();
    WaitUntilList::InsertCurrent(); // ***** insert into WUList
    _wait_until = true;         // is in WUlist
    Passivate();                // deactivation = wait
    return true;                // repeat test (after activation)
  }
}

////////////////////////////////////////////////////////////////////////////
// _WaitUntilRemove() --- remove process from WUlist (called from destructor)
//
void Process::_WaitUntilRemove() {
    if(_wait_until) 
       WaitUntilList::Remove(this);
    _wait_until = false; // is not in WUlist
}


////////////////////////////////////////////////////////////////////////////
// InsertCurrent --- insert current process reference into WUlist 
//                   (only called from Process::_WaitUntil)
//
void WaitUntilList::InsertCurrent()
{
    if(flag) return; // is in WUlist
    //CONDITION: current process is not in WUlist
    Process *e = (Process*) SIMLIB_Current; // static_cast<>
    dprintf(("WaitUntilList.Insert(%s)", e->Name()));
    if(instance==0) 
        create(); // create singleton instance
    if(empty())   // it was empty
        INSTALL_HOOK(WUget_next, WaitUntilList::WU_hook); // install hook
    iterator pos; 
    for( pos = begin(); // find place from beginning
         pos != end() && (*pos)->Priority >= e->Priority;  // higher first
         ++pos );
    instance->l.insert(pos,e);  // insert at position
    //e->_wait_until = true; // mark process as inserted
}

////////////////////////////////////////////////////////////////////////////
// GetCurrent --- get selected process from WUlist
//                (called from Process::* )
//
void WaitUntilList::GetCurrent()
{
  if(!flag) return; // process is not in WUlist
  //PRECONDITION: WUlist is initialized, not empty 
  Process *p = *current;
  dprintf(("WaitUntilList.Get(); // \"%s\" ", p->Name()));
  instance->l.erase(current); // remove item pointed by iterator (fast)
  if(empty())
    INSTALL_HOOK(WUget_next, 0); // uninstall hook if last item removed 
  flag = false;           // iterator invalid, start from beginning
}

////////////////////////////////////////////////////////////////////////////
// clear --- remove all records (called from Init())
//
void WaitUntilList::clear()
{
    if(instance==0) return; 
    // remove all processes in WaitUntilList
    // we can do this, because all processes in list are passivated
    iterator i=begin(); 
    while(i!=end()) { // destroy all processes in WUlist
       Process *p = *i;
       ++i;
       p->_WaitUntilRemove();        // unmark and remove process
       if( p->IsAllocated() ) delete p; // the same behavior as Calendar###???
    }
    if(!instance->l.empty())
        SIMLIB_internal_error(); // for sure
    INSTALL_HOOK(WUget_next, 0); // uninstall hook if empty 
}






















#else // old waul

static int  _WUFlag = false;      // in-test flag ###

class WaitUntilList;              // (internal)

////////////////////////////////////////////////////////////////////////////
// class WUEventNotice = item of WaitUntilList
//
class WUEventNotice : public Link {
 public:
  Process *Ent;         // which process has record
//
  WUEventNotice(Process *e) : Link(0,0,0) { Ent = e; }
  ~WUEventNotice() { // remove from WUList
    /*if(where()!=0)*/ Out();      
  }
  void Output() {};
  virtual const char * Name() { return "WUEventNotice"; }
//
  friend class WaitUntilList;
};

////////////////////////////////////////////////////////////////////////////
// class WaitUntilList
//
class WaitUntilList : List {
public:
  typedef List::iterator iterator;
  WaitUntilList();
  ~WaitUntilList();
  virtual void Output() {};
  virtual const char *Name()  { return "WaitUntilList"; };
  List::empty;               // public
  List::begin;               // public
  List::end;                 // public
  void Insert(Process *p);   // insert p into list
  Process *GetCurrent();     // get active process
  void clear();              // empty
  List::iterator act; // activity
  bool active() { return act!=end(); }
  Process *front() { 
    act=List::begin(); 
    return active() ? ((WUEventNotice*)*act)->Ent : 0; 
  }
  Process *next() { 
    ++act; 
    return active() ? ((WUEventNotice*)*act)->Ent : 0; 
  }
};

////////////////////////////////////////////////////////////////////////////
// object WaitUntilList
//
static WaitUntilList WUList;   // unique list

#ifndef NDEBUG
void WU_print() {
   WaitUntilList::iterator i = WUList.begin();
   for( int n=0 ; i!=WUList.end() ; ++i, ++n )
     _Print(" [%d] %s\n", n, ((WUEventNotice*)*i)->Ent->Name() );
}
#endif


////////////////////////////////////////////////////////////////////////////
// SIMLIB_DoActions -- central calling of interruptible procedures !!!
//
// Warning!!!:
//     SIMLIB_Current->_Run() _must_ be called only from this function!!!
//     achieved by friendship
//
void SIMLIB_DoActions()
{
  SIMLIB_Current->_Run();           // perform event -- dispatch
  // test all WaitUntil conditions
  if (!WUList.empty()) {
    dprintf(("*START SIMLIB_WaitUntilTests"));
    SIMLIB_Current = WUList.front();
    while(SIMLIB_Current!=0) {
      _WUFlag = true; // Current is from WAUL, not Calendar!
      SIMLIB_Current->_Run();  // problem deleting active item!!!
      _WUFlag = false;
      if(!WUList.active())     // activity lost
        SIMLIB_Current = WUList.front(); // activity to first ###!!!
      else
        SIMLIB_Current = WUList.next(); // activity to next
    }
    dprintf(("*END SIMLIB_WaitUntilTests"));
  }
}

////////////////////////////////////////////////////////////////////////////
// _WaitUntil -- wait to condition
//
int Process::_WaitUntil(int test)
{
  dprintf(("%s._WaitUntil(%s)",Name(),test?"true":"false"));
  if(test) {   // true -- end of wait
    if (_WUFlag)  WUList.GetCurrent();  // from WUList
    return false;                       // continue
  } else {     // false -- wait
    if (!_WUFlag) WUList.Insert(this);  // into WUList
    Passivate();                        // deactivation = wait
    return true;                        // repeat test (after activation)
  }
}
////////////////////////////////////////////////////////////////////////////
//  Insert -- in WUList
//
void WaitUntilList::Insert(Process *e)
{
  dprintf(("WaitUntilList.Insert(%s)",e->Name()));
  WUEventNotice *evn = new WUEventNotice(e);
  Entity::tPriority prio = e->Priority;
  List::iterator p = begin();
  for( ; 
        p!=end() && 
	((WUEventNotice *)(*p))->Ent->Priority >= prio;  // higher first
        ++p);
  List::PredIns((Link*)evn,*p);
}

////////////////////////////////////////////////////////////////////////////
//  GetCurrent -- get current process record
//
Process *WaitUntilList::GetCurrent()
{
  if(empty()) 
      SIMLIB_error(EmptyWUListError);
  Process *e = ((WUEventNotice*)*act)->Ent;
  dprintf(("WaitUntilList.GetCurrent(); // \"%s\" ", e->Name()));
  delete *act;     // out of list, disconnect entity, remove
  act = begin();   // reset activity
  _WUFlag = false; // must be!!! ###???
  return e;        // entity
}
////////////////////////////////////////////////////////////////////////////
//  SIMLIB_WUClear -- remove all items in WaitUntil list
//
void SIMLIB_WUClear()
{
  WUList.clear();
}

////////////////////////////////////////////////////////////////////////////
// constructor
//
WaitUntilList::WaitUntilList() : List("WaitUntilList"), act(begin())
{
  dprintf(("WaitUntilList::WaitUntilList()"));
  if (this!=&WUList) SIMLIB_internal_error();
}

////////////////////////////////////////////////////////////////////////////
// destructor
//
WaitUntilList::~WaitUntilList()
{
  dprintf(("WaitUntilList::~WaitUntilList()"));
  clear();                          // remove
}

////////////////////////////////////////////////////////////////////////////
//  Clear -- remove all records
//
void WaitUntilList::clear()
{
  while(!empty()) {
    WUEventNotice *evn = (WUEventNotice*)GetFirst();  // select
    Process *e = evn->Ent;
    delete evn;                        // delete record
    if (e->IsAllocated()) delete e;       // delete entity
  }
}

#endif // new WUlist

// end

