// $Id$
// Associative array

// Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
// Written by Andreas Zeller <zeller@gnu.org>.
// 
// This file is part of DDD.
// 
// DDD 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 3 of the License, or (at your option) any later version.
// 
// DDD 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.
// 
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, see <http://www.gnu.org/licenses/>.
// 
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page, 
// `http://www.gnu.org/software/ddd/',
// or send a mail to the DDD developers <ddd@gnu.org>.

#ifndef _DDD_Assoc_h
#define _DDD_Assoc_h

#include "bool.h"
#include "assert.h"

#include <stdlib.h>		// abort()

template<class K, class V> class AssocMark;
template<class K, class V> class _Assoc;
template<class K, class V> class Assoc;
template<class K, class V> class AssocIter;

template<class K, class V>
class AssocRec {
    friend class _Assoc<K,V>;
    friend class Assoc<K,V>;
    friend class AssocIter<K,V>;

private:
    AssocRec<K,V> *next;		// For Assoc usage only

public:
    K key;
    V value;
    
    // Constructor
    AssocRec(const K& k, const V& v)
	: next(0), key(k), value(v)
    {}
    AssocRec(const K& k)
	: next(0), key(k)
    {}

private:
    AssocRec(const AssocRec<K, V>&);
    AssocRec<K,V>& operator = (const AssocRec<K, V>&);
};

template<class K, class V>
class _Assoc {
    friend class AssocMark<K,V>;

protected:
    AssocRec<K,V> *entries;	// Entries

    virtual AssocRec<K,V> *lookup(const K& key) const
    {
	for (AssocRec<K,V> *e = entries; e != 0; e = e->next)
	    if (key == e->key)
		return e;

	return 0;
    }

    virtual AssocRec<K,V> *insert(const K& key)
    {
	AssocRec<K,V> *e = new AssocRec<K,V>(key);
	e->next = entries;
	return entries = e;
    }

private:
    // Destroy all elements
    void destroyAll()
   {
      AssocRec<K,V> *next = 0;
      for (AssocRec<K,V> *e = entries; e != 0; e = next)
      {
          next = e->next;
          delete e;
      }
      entries = 0;
   }

public:
    // Constructors
    _Assoc():
	entries(0)
    {}

    // Destructor
    virtual ~_Assoc()
    {
	destroyAll();
    }

    // Resources

    // Access (or create) element KEY
    V& operator[] (const K& key)
    {
	AssocRec<K,V> *e = lookup(key);
	if (e == 0)
	    e = insert(key);
	return e->value;
    }

    // Access element KEY; abort if non-existent
    const V& operator[] (const K& key) const
    {
	AssocRec<K,V> *e = lookup(key);
	if (e == 0)
	    abort();
	return e->value;
    }

    // Add element KEY
    void add(const K& key)
    {
	insert(key);
    }

    // Check if element KEY is present
    bool has(const K& key) const
    {
	return lookup(key) != 0;
    }

    // Remove up to N elements KEY
    void remove(const K& key, int n = -1)
    {
	if (n == 0)
	    return;

	AssocRec<K,V> *prev = 0;
	AssocRec<K,V> *e = entries;
	while (e != 0)
	{
	    AssocRec<K,V> *next = e->next;

	    if (key == e->key)
	    {
		if (prev == 0)
		    entries = next;
		else
		    prev->next = next;
		delete e;

		if (--n == 0)
		    return;
	    }
	    else
	    {
		prev = e;
	    }

	    e = next;
	}
    }

    // Copy constructor
    _Assoc(const _Assoc<K,V>& m):
	entries(0)
    {
	for (AssocRec<K,V> *e = m.entries; e != 0; e = e->next)
	    (*this)[e->key] = e->value;
    }

    // Assignment
    _Assoc<K, V>& operator = (const _Assoc<K,V>& m)
    {
	if (this != &m)
	{
	    if (entries)
		destroyAll();
	    entries = 0;

	    for (AssocRec<K,V> *e = m.entries; e != 0; e = e->next)
		(*this)[e->key] = e->value;
	}

	return *this;
    }
};


template<class K, class V>
class AssocMark {
    friend class Assoc<K,V>;

protected:
    AssocRec<K,V> *rec;

    AssocMark(AssocRec<K,V> *ptr):
	rec(ptr)
    {}

public:
    AssocMark(const Assoc<K,V>& assoc):
	rec(assoc.entries)
    {}
    AssocMark(const AssocMark<K,V>& mark):
	rec(mark.rec)
    {}
    AssocMark<K,V>& operator = (const Assoc<K,V>& assoc)
    {
	rec = assoc.entries;
	return *this;
    }
    AssocMark<K,V>& operator = (const AssocMark<K,V>& mark)
    {
        if (this != &mark)
	  rec = mark.rec;
	return *this;
    }
};


template<class K, class V>
class AssocIter: public AssocMark<K,V> {
protected:
    AssocIter(AssocRec<K,V> *ptr):
	AssocMark<K,V>(ptr)
    {}

public:
    AssocIter(const Assoc<K,V>& assoc):
	AssocMark<K,V>(assoc)
    {}
    AssocIter(const AssocIter<K,V>& iter):
	AssocMark<K,V>(iter)
    {}
    AssocIter<K,V>& operator = (const Assoc<K,V>& assoc)
    {
	AssocMark<K,V>::operator = (assoc);
	return *this;
    }
    AssocIter<K,V>& operator = (const AssocIter<K,V>& iter)
    {
        if (this != &iter)
	  AssocMark<K,V>::operator = (iter);
	return *this;
    }

    const K& key() const   { return this->rec->key;   }
    const V& value() const { return this->rec->value; }
    V& value()             { return this->rec->value; }

    bool ok() const { return this->rec != 0; }
    AssocIter<K,V> next() const { return AssocIter<K,V>(this->rec->next); }
    const AssocIter<K,V>& operator ++ ()
    {
	this->rec = this->rec->next; return *this;
    }
    const AssocIter<K,V> operator ++ (int)
    {
	AssocIter<K,V> old(*this);
	this->rec = this->rec->next;
	return old;
    }
};


template<class K, class V>
class Assoc: public _Assoc<K, V>
{
protected:
    AssocRec<K,V> *lookup(const K& key) const
    {
	return _Assoc<K, V>::lookup(key);
    }

    AssocRec<K,V> *insert(const K& key)
    {
	return _Assoc<K, V>::insert(key);
    }

public:
    // Truncate array up to assoc iterator
    void release (const AssocMark<K, V>& mark)
    {
	AssocRec<K, V> *e = this->entries;

	while (e != 0 && e != mark.rec)
	{
	    AssocRec<K, V> *tmp = e;
	    e = e->next;
	    tmp->next = 0;
	    delete tmp;
	}

	this->entries = e;
    }

    // Constructor
    Assoc()
	: _Assoc<K, V>() 
    {}

    // Destructor
    ~Assoc() {}

    // Copy
    Assoc(const Assoc<K, V>& m)
	: _Assoc<K, V>(m)
    {}

    // Assignment
    Assoc<K, V>& operator = (const Assoc<K, V>& m)
    {
        if (this != &m) {
	  _Assoc<K, V>::operator =(m);
	}
	return *this;
    }
};

#endif // _DDD_Assoc_h
// DON'T ADD ANYTHING BEHIND THIS #endif
