// List of unprocessed states -*- c++ -*-

#ifndef STATELIST_H_
# define STATELIST_H_
# ifdef __GNUC__
#  pragma interface
# endif // __GNUC__

# include <list>
# include "BitBuffer.h"

/** @file StateList.h
 * List of unprocessed states
 */

/* Copyright © 2001-2002 Marko Mäkelä (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   MARIA is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/** List of unprocessed states */
class StateList
{
public:
  /** An unprocessed state stored in the list */
  struct item
  {
    /** the encoded state */
    word_t* data;
    /** length of the encoded state, in bytes */
    size_t size;
    /** offset to the counterexample path file */
    long offset;
  };
  typedef std::list<struct item> List;
  typedef List::const_iterator const_iterator;
  /** Constructor */
  StateList () : myList () {}
private:
  /** Copy constructor */
  StateList (const class StateList& old);
  /** Assignment operator */
  class StateList& operator= (const class StateList& old);
public:
  /** Destructor */
  ~StateList () {
    clear ();
  }

  /** Determine whether the list is empty */
  bool empty () const { return myList.empty (); }
  /** Clear the list */
  void clear () {
    for (List::iterator i = myList.begin (); i != myList.end (); i++)
      delete[] i->data;
    myList.clear ();
  }
  /** Clear the list, not deallocating the states */
  void clear_allocated () { myList.clear (); }
  /** Insert an encoded state to the list
   * @param buf		the encoded state
   * @param len		length of the encoded state in bytes
   * @param offset	offset to the counterexample path file
   */
  void push (const void* buf,
	     size_t len,
	     long offset) {
    size_t last = (len - 1) / sizeof (word_t);
    struct item i = { buf ? new word_t[last + 1] : 0, len, offset };
    if (buf) {
      i.data[last] = 0;
      memcpy (i.data, buf, len);
    }
    myList.push_front (i);
  }
  /** Insert an encoded state to the list
   * @param buf		the encoded state (new copy)
   * @param len		length of the encoded state in bytes
   * @param offset	offset to the counterexample path file
   */
  void push_allocated (word_t* buf,
		       size_t len,
		       long offset) {
    struct item i = { buf, len, offset };
    myList.push_front (i);
  }
  /** Push all states of a list to the list
   * @param other	the queue to be pushed to this
   */
  void push (class StateList& other) {
    for (List::iterator i = other.myList.begin ();
	 i != other.myList.end (); i++)
      myList.push_back (*i);
    other.myList.clear ();
  }
  /** Fetch an encoded state from the list
   * @param tail	flag: retrieve from tail of list instead of head
   * @param offset	(output) offset to the counterexample path file
   * @param len		(output) length of the encoded stream (optional)
   * @return		the encoded state
   */
  word_t* pop (bool tail,
	       long& offset,
	       size_t* len = 0) {
    List::iterator i;
    if (tail)
      (i = myList.end ())--;
    else
      i = myList.begin ();
    offset = i->offset;
    word_t* data = i->data;
    if (len)
      *len = i->size;
    else
      BitPacker::inflate (data[(i->size - 1) / sizeof (word_t)],
			  (-i->size) % sizeof (word_t));
    myList.erase (i);
    return data;
  }
  /** Dequeue an item from the list
   * @param tail	flag: retrieve from tail of list instead of head
   * @param i		(optional output) the dequeued item
   */
  void pop (bool tail,
	    struct item* i) {
    List::iterator l;
    if (tail)
      (l = myList.end ())--;
    else
      l = myList.begin ();
    if (i)
      *i = *l;
    myList.erase (l);
  }
  /** @name accessors to the list */
  /*@{*/
  const_iterator begin () const { return myList.begin (); }
  const_iterator end () const { return myList.end (); }
  size_t size () const { return myList.size (); }
  /*@}*/

private:
  /** The list of unprocessed states */
  List myList;
};

#endif // STATELIST_H_
