/*
 * Dillo Widget
 *
 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
 * Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __LOUT_CONTAINER_HH_
#define __LOUT_CONTAINER_HH_

#include "object.hh"

namespace lout {

/**
 * \brief This namespace contains a framework for container classes, which
 *    members are instances of object::Object.
 *
 * A common problem in languages without garbage collection is, where the
 * children belong to, and so, who is responsible to delete them (instantiation
 * is always done by the caller). This information is here told to the
 * collections, each container has a constructor with the parameter
 * "ownerOfObjects" (HashTable has two such parameters, for keys and values).
 *
 * \sa container::untyped, container::typed
 */
namespace container {

/**
 * \brief The container classes defined here contain instances of
 *    object::Object.
 *
 * Different sub-classes may be mixed, and you have to care about casting,
 * there is no type-safety.
 */
namespace untyped {

/**
 * \brief ...
 */
class Collection0: public object::Object
{
   friend class Iterator;

protected:
   /**
    * \brief The base class for all iterators, as created by
    *   container::untyped::Collection::createIterator.
    */
   class AbstractIterator: public object::Object
   {
   private:
      int refcount;

   public:
      AbstractIterator() { refcount = 1; }

      void ref () { refcount++; }
      void unref () { refcount--; if (refcount == 0) delete this; }

      virtual bool hasNext () = 0;
      virtual Object *getNext () = 0;
   };

protected:
   virtual AbstractIterator* createIterator() = 0;
};

/**
 * \brief This is a small wrapper for AbstractIterator, which may be used
 *    directly, not as a pointer, to makes memory management simpler.
 */
class Iterator
{
   friend class Collection;

private:
   Collection0::AbstractIterator *impl;

   // Should not instantiated directly.
   inline Iterator(Collection0::AbstractIterator *impl) { this->impl = impl; }

public:
   Iterator();
   Iterator(const Iterator &it2);
   Iterator(Iterator &it2);
   ~Iterator();
   Iterator &operator=(const Iterator &it2);
   Iterator &operator=(Iterator &it2);

   inline bool hasNext() { return impl->hasNext(); }
   inline object::Object *getNext() { return impl->getNext(); }
};

/**
 * \brief Abstract base class for all container objects in container::untyped.
 */
class Collection: public Collection0
{
public:
   void intoStringBuffer(misc::StringBuffer *sb);
   inline Iterator iterator() { Iterator it(createIterator()); return it; }
   virtual int size() = 0;
};


/**
 * \brief Container, which is implemented by an array, which is
 *    dynamically resized.
 */
class Vector: public Collection
{
   friend class VectorIterator;

private:
   object::Object **array;
   int numAlloc, numElements;
   bool ownerOfObjects;

   class VectorIterator: public AbstractIterator
   {
   private:
      Vector *vector;
      int index;

   public:
      VectorIterator(Vector *vector) { this->vector = vector; index = -1; }
      bool hasNext();
      Object *getNext();
   };

protected:
   AbstractIterator* createIterator();

public:
   Vector(int initSize, bool ownerOfObjects);
   ~Vector();

   int size();

   void put(object::Object *newElement, int newPos = -1);
   void insert(object::Object *newElement, int pos);

   /**
    * \brief Insert into an already sorted vector.
    *
    * Notice that insertion is not very efficient, unless the position
    * is rather at the end.
    */
   inline int insertSorted(object::Object *newElement,
                           object::Comparator *comparator =
                           &object::standardComparator)
   { int pos = bsearch (newElement, false, comparator);
      insert (newElement, pos); return pos; }

   void remove(int pos);
   inline object::Object *get(int pos) const
   { return (pos >= 0 && pos < numElements) ? array[pos] : NULL; }
   void clear();
   void sort(object::Comparator *comparator = &object::standardComparator);
   int bsearch(Object *key, bool mustExist, int start, int end,
               object::Comparator *comparator = &object::standardComparator);
   inline int bsearch(Object *key, bool mustExist,
                      object::Comparator *comparator =
                      &object::standardComparator)
   { return bsearch (key, mustExist, 0, size () - 1, comparator); }
};


/**
 * \brief A single-linked list.
 */
class List: public Collection
{
   friend class ListIterator;

private:
   struct Node
   {
   public:
      object::Object *object;
      Node *next;
   };

   class ListIterator: public AbstractIterator
   {
   private:
      List::Node *current;
   public:
      ListIterator(List::Node *node) { current = node; }
      bool hasNext();
      Object *getNext();
   };

   Node *first, *last;
   bool ownerOfObjects;
   int numElements;

   bool remove0(object::Object *element, bool compare, bool doNotDeleteAtAll);

protected:
   AbstractIterator* createIterator();

public:
   List(bool ownerOfObjects);
   ~List();

   bool equals(Object *other);
   int hashValue();

   int size ();

   void clear();
   void append(object::Object *element);
   bool insertBefore(object::Object *beforeThis, object::Object *neew);
   inline bool removeRef(object::Object *element)
   { return remove0(element, false, false); }
   inline bool remove(object::Object *element)
   { return remove0(element, true, false); }
   inline bool detachRef(object::Object *element)
   { return remove0(element, false, true); }
   inline int size() const { return numElements; }
   inline bool isEmpty() const { return numElements == 0; }
   inline object::Object *getFirst() const { return first->object; }
   inline object::Object *getLast() const { return last->object; }
};


/**
 * \brief A hash set.
 */
class HashSet: public Collection
{
   friend class HashSetIterator;

protected:
   struct Node
   {
      object::Object *object;
      Node *next;
      virtual ~Node() {};
   };

   Node **table;
   int tableSize, numElements;
   bool ownerOfObjects;

   inline int calcHashValue(object::Object *object) const
   {
      return abs(object->hashValue()) % tableSize;
   }

   virtual Node *createNode();
   virtual void clearNode(Node *node);

   Node *findNode(object::Object *object) const;
   Node *insertNode(object::Object *object);

   AbstractIterator* createIterator();

private:
   class HashSetIterator: public Collection0::AbstractIterator
   {
   private:
      HashSet *set;
      HashSet::Node *node;
      int pos;

      void gotoNext();

   public:
      HashSetIterator(HashSet *set);
      bool hasNext();
      Object *getNext();
   };

public:
   HashSet(bool ownerOfObjects, int tableSize = 251);
   ~HashSet();

   int size ();

   void put (object::Object *object);
   bool contains (object::Object *key) const;
   bool remove (object::Object *key);
   //Object *getReference (object::Object *object);
};

/**
 * \brief A hash table.
 */
class HashTable: public HashSet
{
private:
   bool ownerOfValues;

   struct KeyValuePair: public Node
   {
      object::Object *value;
   };

protected:
   Node *createNode();
   void clearNode(Node *node);

public:
   HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251);
   ~HashTable();

   void intoStringBuffer(misc::StringBuffer *sb);

   void put (object::Object *key, object::Object *value);
   object::Object *get (object::Object *key) const;
};

/**
 * \brief A stack (LIFO). Can be used as Queue (FIFO) when pushUnder()
 *     is used instead of push().
 *
 * Note that the iterator returns all elements in the reversed order they have
 * been put on the stack.
 */
class Stack: public Collection
{
   friend class StackIterator;

private:
   class Node
   {
   public:
      object::Object *object;
      Node *prev;
   };

   class StackIterator: public AbstractIterator
   {
   private:
      Stack::Node *current;
   public:
      StackIterator(Stack::Node *node) { current = node; }
      bool hasNext();
      Object *getNext();
   };

   Node *bottom, *top;
   bool ownerOfObjects;
   int numElements;

protected:
   AbstractIterator* createIterator();

public:
   Stack (bool ownerOfObjects);
   ~Stack();

   int size ();

   void push (object::Object *object);
   void pushUnder (object::Object *object);
   inline object::Object *getTop () const { return top ? top->object : NULL; }
   void pop ();
   inline int size() const { return numElements; }
};

} // namespace untyped

/**
 * \brief This namespace provides thin wrappers, implemented as C++ templates,
 *    to gain type-safety.
 *
 * Notice, that nevertheless, all objects still have to be instances of
 * object::Object.
 */
namespace typed {

template <class T> class Collection;

/**
 * \brief Typed version of container::untyped::Iterator.
 */
template <class T> class Iterator
{
   friend class Collection<T>;

private:
   untyped::Iterator base;

public:
   inline Iterator() { }
   inline Iterator(const Iterator<T> &it2) { this->base = it2.base; }
   inline Iterator(Iterator<T> &it2) { this->base = it2.base; }
   ~Iterator() { }
   inline Iterator &operator=(const Iterator<T> &it2)
   { this->base = it2.base; return *this; }
   inline Iterator &operator=(Iterator<T> &it2)
   { this->base = it2.base; return *this; }

   inline bool hasNext() { return this->base.hasNext(); }
   inline T *getNext() { return (T*)this->base.getNext(); }
};

/**
 * \brief Abstract base class template for all container objects in
 *    container::typed.
 *
 * Actually, a wrapper for container::untyped::Collection.
 */
template <class T> class Collection: public object::Object
{
protected:
   untyped::Collection *base;

public:
   Collection () { this->base = NULL; }
   ~Collection () { if (this->base) delete this->base; }

   bool equals(Object *other)
   { return this->base->equals (((Collection<T>*)other)->base); }

   int hashValue() { return this->base->hashValue (); }

   void intoStringBuffer(misc::StringBuffer *sb)
   { this->base->intoStringBuffer(sb); }
   inline Iterator<T> iterator() {
      Iterator<T> it; it.base = this->base->iterator(); return it; }
   inline int size() { return this->base->size (); }
};


/**
 * \brief Typed version of container::untyped::Vector.
 */
template <class T> class Vector: public Collection <T>
{
public:
   inline Vector(int initSize, bool ownerOfObjects) {
      this->base = new untyped::Vector(initSize, ownerOfObjects); }

   inline void put(T *newElement, int newPos = -1)
   { ((untyped::Vector*)this->base)->put(newElement, newPos); }
   inline void insert(T *newElement, int pos)
   { ((untyped::Vector*)this->base)->insert(newElement, pos); }
   inline int insertSorted(T *newElement,
                           object::Comparator *comparator =
                           &object::standardComparator)
   { return ((untyped::Vector*)this->base)->insertSorted(newElement,
                                                         comparator); }
   inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); }
   inline T *get(int pos) const
   { return (T*)((untyped::Vector*)this->base)->get(pos); }
   inline void clear() { ((untyped::Vector*)this->base)->clear(); }
   inline void sort(object::Comparator *comparator =
                    &object::standardComparator)
   { ((untyped::Vector*)this->base)->sort(comparator); }
   inline int bsearch(T *key, bool mustExist, int start, int end,
                      object::Comparator *comparator =
                      &object::standardComparator)
   { return ((untyped::Vector*)this->base)->bsearch(key, mustExist, start, end,
                                                    comparator); }
   inline int bsearch(T *key, bool mustExist,
                      object::Comparator *comparator =
                      &object::standardComparator)
   { return ((untyped::Vector*)this->base)->bsearch(key, mustExist,
                                                    comparator); }
};


/**
 * \brief Typed version of container::untyped::List.
 */
template <class T> class List: public Collection <T>
{
public:
   inline List(bool ownerOfObjects)
   { this->base = new untyped::List(ownerOfObjects); }

   inline void clear() { ((untyped::List*)this->base)->clear(); }
   inline void append(T *element)
   { ((untyped::List*)this->base)->append(element); }
   inline bool insertBefore(object::Object *beforeThis, object::Object *neew)
   { return ((untyped::List*)this->base)->insertBefore(beforeThis, neew); }
   inline bool removeRef(T *element) {
      return ((untyped::List*)this->base)->removeRef(element); }
   inline bool remove(T *element) {
      return ((untyped::List*)this->base)->remove(element); }
   inline bool detachRef(T *element) {
      return ((untyped::List*)this->base)->detachRef(element); }

   inline bool isEmpty() const
   { return ((untyped::List*)this->base)->isEmpty(); }
   inline T *getFirst() const
   { return (T*)((untyped::List*)this->base)->getFirst(); }
   inline T *getLast() const
   { return (T*)((untyped::List*)this->base)->getLast(); }
};

/**
 * \brief Typed version of container::untyped::HashSet.
 */
template <class T> class HashSet: public Collection <T>
{
protected:
   inline HashSet() { }

public:
   inline HashSet(bool owner, int tableSize = 251)
   { this->base = new untyped::HashSet(owner, tableSize); }

   inline void put(T *object)
   { return ((untyped::HashSet*)this->base)->put(object); }
   inline bool contains(T *object) const
   { return ((untyped::HashSet*)this->base)->contains(object); }
   inline bool remove(T *object)
   { return ((untyped::HashSet*)this->base)->remove(object); }
   //inline T *getReference(T *object)
   //{ return (T*)((untyped::HashSet*)this->base)->getReference(object); }
};

/**
 * \brief Typed version of container::untyped::HashTable.
 */
template <class K, class V> class HashTable: public HashSet <K>
{
public:
   inline HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251)
   { this->base = new untyped::HashTable(ownerOfKeys, ownerOfValues,
                                         tableSize); }

   inline void put(K *key, V *value)
   { return ((untyped::HashTable*)this->base)->put(key, value); }
   inline V *get(K *key) const
   { return (V*)((untyped::HashTable*)this->base)->get(key); }
};

/**
 * \brief Typed version of container::untyped::Stack.
 */
template <class T> class Stack: public Collection <T>
{
public:
   inline Stack (bool ownerOfObjects)
   { this->base = new untyped::Stack (ownerOfObjects); }

   inline void push (T *object) {
      ((untyped::Stack*)this->base)->push (object); }
   inline void pushUnder (T *object)
   { ((untyped::Stack*)this->base)->pushUnder (object); }
   inline T *getTop () const
   { return (T*)((untyped::Stack*)this->base)->getTop (); }
   inline void pop () { ((untyped::Stack*)this->base)->pop (); }
};

} // namespace untyped

} // namespace container

} // namespace lout

#endif // __LOUT_CONTAINER_HH_
