// Copyright (C) 1999-2005 Open Source Telecom Corporation.
// Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
// Copyright (C) 2015-2020 Cherokees of Idaho.
//
// 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 2 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 Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// Common C++.  If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

/**
 * @file object.h
 * @short Some object manipulation classes for smart pointers, linked lists,
 * etc.
 **/

#ifndef COMMONCPP_OBJECT_H_
#define COMMONCPP_OBJECT_H_

#ifndef COMMONCPP_CONFIG_H_
#include <commoncpp/config.h>
#endif

namespace ost {

class MapObject;
class MapIndex;

/**
 * A reference countable object.  This is used in association with smart
 * pointers (RefPointer).
 *
 * @author David Sugar <dyfet@gnutelephony.org>
 * @short Object managed by smart pointer reference count.
 */
class __EXPORT RefObject
{
private:
    __DELETE_COPY(RefObject);

protected:
    friend class RefPointer;

    unsigned refCount;

    /**
     * The constructor simply initializes the count.
     */
    inline RefObject() {
        refCount = 0;
    }

    /**
     * The destructor is called when the reference count returns
     * to zero.  This is done through a virtual destructor.
     */
    virtual ~RefObject();

public:
    /**
     * The actual object being managed can be returned by this
     * method as a void and then recast to the actual type.  This
     * removes the need to dynamic cast from RefObject and the
     * dependence on rtti this implies.
     *
     * @return underlying object being referenced.
     */
    virtual void *getObject(void) = 0;
};

/**
 * Pointer to reference counted objects.  This is a non-template form
 * of a reference count smart pointer, and so uses common code.  This
 * can be subclassed to return explicit object types.
 *
 * @author David Sugar <dyfet@gnutelephony.org>
 * @short Pointer to reference count managed objects.
 */
class __EXPORT RefPointer
{
protected:
    RefObject *ref;

    /**
     * Detach current object, for example, when changing pointer.
     */
    void detach(void);

    /**
     * Patch point for mutex in derived class.  This may often
     * be a single static mutex shared by a managed type.
     */
    virtual void enterLock(void);

    /**
     * Patch point for a mutex in derived class.  This may often
     * be a single static mutex shared by a managed type.
     */
    virtual void leaveLock(void);

public:
    /**
     * Create an unattached pointer.
     */
    inline RefPointer() {
        ref = NULL;
    }

    /**
     * Create a pointer attached to a reference counted object.
     *
     * Object being referenced.
     */
    RefPointer(RefObject *obj);

    /**
     * A copy constructor.
     *
     * Pointer being copied.
     */
    RefPointer(const RefPointer &ptr);

    virtual ~RefPointer();

    RefPointer& operator=(const RefObject &ref);

    inline void *operator*() const {
        return getObject();
    }

    void *getObject(void) const;

    operator bool() const;

    bool operator!() const;
};

/**
 * Self managed single linked list object chain.  This is used for
 * accumulating lists by using as a base class for a derived subclass.
 *
 * @author David Sugar <dyfet@gnutelephony.org>
 * @short Accumulating single linked list.
 */
class __EXPORT LinkedSingle
{
private:
    __DELETE_COPY(LinkedSingle);

protected:
    LinkedSingle *nextObject;

    inline LinkedSingle() {
        nextObject = NULL;
    }

    virtual ~LinkedSingle();

public:
    /**
     * Get first linked object in list.  This may be dynamically
     * recast, and may refer to a master static bookmark pointer
     * in a derived class.  Otherwise it simply returns the current
     * object.  In a "free" list, this may not only return the first
     * object, but also set the first to next.
     *
     * @return pointer to first object in list.
     */
    virtual LinkedSingle *getFirst(void);

    /**
     * Gets the last object in the list.  This normally follows the
     * links to the end.  This is a virtual because derived class
     * may include a static member bookmark for the current end.
     *
     * @return pointer to last object in list.
     */
    virtual LinkedSingle *getLast(void);

    /**
     * Get next object, for convenience.  Derived class may use
     * this with a dynamic cast.
     *
     * @return next object in list.
     */
    inline LinkedSingle *getNext(void) {
        return nextObject;
    }

    /**
     * Insert object into chain.  This is a virtual because
     * derived class may choose instead to perform an insert
     * at head or tail, may manage bookmarks, and may add mutex lock.
     *
     * @param object being inserted.
     */
    virtual void insert(LinkedSingle& obj);

    LinkedSingle &operator+=(LinkedSingle &obj);
};

/**
 * Self managed double linked list object chain.  This is used for
 * accumulating lists by using as a base class for a derived subclass.
 *
 * @author David Sugar <dyfet@gnutelephony.org>
 * @short Accumulating double linked list.
 */
class __EXPORT LinkedDouble
{
private:
    __DELETE_COPY(LinkedDouble);

protected:
    LinkedDouble *nextObject, *prevObject;

    inline LinkedDouble() {
        nextObject = prevObject = NULL;
    }

    virtual ~LinkedDouble();

    virtual void enterLock(void);

    virtual void leaveLock(void);

    virtual LinkedDouble *firstObject();

    virtual LinkedDouble *lastObject();

public:

    /**
    * Requested in overloaded insert() method to indicate how to insert
    * data into list
    */
    enum InsertMode
    {
      modeAtFirst,  /**< insert at first position in list pointed by current object */
      modeAtLast,   /**< insert at last position in list pointed by current object */
      modeBefore,   /**< insert in list before current object */
      modeAfter     /**< insert in list after current object */
    };

    /**
     * Get first linked object in list.  This may be dynamically
     * recast, and may refer to a master static bookmark pointer
     * in a derived class.  Otherwise it follows list to front.
     *
     * @return pointer to first object in list.
     */
    virtual LinkedDouble *getFirst(void);

    /**
     * Gets the last object in the list.  This normally follows the
     * links to the end.  This is a virtual because derived class
     * may include a static member bookmark for the current end.
     *
     * @return pointer to last object in list.
     */
    virtual LinkedDouble *getLast(void);

    /**
     * Virtual to get the insert point to use when adding new members.  This
     * may be current, or always head or always tail.  As a virtual, this allows
     * derived class to establish "policy".
     *
     * @return pointer to insertion point in list.
     */
    virtual LinkedDouble *getInsert(void);

    /**
     * Get next object, for convenience.  Derived class may use
     * this with a dynamic cast.
     *
     * @return next object in list.
     */
    inline LinkedDouble *getNext(void) {
        return nextObject;
    }

    /**
     * Get prev object in the list.
     *
     * @return pointer to previous object.
     */
    inline LinkedDouble *getPrev(void) {
        return prevObject;
    }

    /**
    * Insert object into chain at given pos, as indicated by \ref InsertMode;
    * If no pos is given, it defaults to \ref modeAtLast, inserting element
    * at list's end.
    *
    * @param object being inserted.
    * @param position where object is inserted.
    */
    virtual void insert(LinkedDouble& obj, InsertMode position = modeAtLast);

    /**
     * Remove object from chain.
     */
    virtual void detach(void);

    LinkedDouble &operator+=(LinkedDouble &obj);

    LinkedDouble &operator--();
};

/**
 * A map table allows for entities to be mapped (hash index) onto it.
 * Unlike with Assoc, This form of map table also allows objects to be
 * removed from the table.  This table also includes a mutex lock for
 * thread safety.  A free list is also optionally maintained for reusable
 * maps.
 *
 * @author David Sugar <dyfet@gnutelephony.org>
 * @short Table to hold hash indexed objects.
 */
class __EXPORT MapTable : public Mutex
{
private:
    __DELETE_COPY(MapTable);

protected:
    friend class MapObject;
    friend class MapIndex;
    unsigned range;
    unsigned count;
    MapObject **map;

    void cleanup(void);

public:
    /**
     * Create a map table with a specified number of slots.
     *
     * @param number of slots.
     */
    MapTable(unsigned size);

    /**
     * Destroy the table, calls cleanup.
     */
    virtual ~MapTable();

    /**
     * Get index value from id string.  This function can be changed
     * as needed to provide better collision avoidence for specific
     * tables.
     *
     * @param id string
     * @return index slot in table.
     */
    virtual unsigned getIndex(const char *id);

    /**
     * Return range of this table.
     *
     * @return table range.
     */
    inline unsigned getRange(void) {
        return range;
    }

    /**
     * Return the number of object stored in this table.
     *
     * @return table size.
     */
    inline unsigned getSize(void) {
        return count;
    }

    /**
     * Lookup an object by id key.  It is returned as void * for
     * easy re-cast.
     *
     * @param key to find.
     * @return pointer to found object or NULL.
     */
    void *getObject(const char *id);

    /**
     * Map an object to our table.  If it is in another table
     * already, it is removed there first.
     *
     * @param object to map.
     */
    void addObject(MapObject &obj);
    /**
     * Get the first element into table, it is returned as void * for
     * easy re-cast.
     *
     * @return pointer to found object or NULL.
     */
    void *getFirst();

    /**
     * Get the last element into table, it is returned as void * for
     * easy re-cast.
     *
     * @return pointer to found object or NULL.
     */
    void *getLast();

    /**
     * Get table's end, useful for cycle control; it is returned as void * for
     * easy re-cast.
     *
     * @return pointer to found object or NULL.
     */
    void *getEnd() {
        return NULL;
    }

    /**
     * Get next object from managed free list.  This returns as a
     * void so it can be recast into the actual type being used in
     * derived MapObject's.  A derived version of MapTable may well
     * offer an explicit type version of this.  Some derived
     * MapObject's may override new to use managed list.
     *
     * @return next object on free list.
     */
    void *getFree(void);

    /**
     * Add an object to the managed free list.  Some MapObject's
     * may override delete operator to detach and do this.
     *
     * @param object to add.
     */
    void addFree(MapObject *obj);

    /**
     * An operator to map an object to the table.
     *
     * @return table being used.
     * @param object being mapped.
     */
    MapTable &operator+=(MapObject &obj);

    /**
     * This operator is virtual in case it must also add the object to a
     * managed free list.
     *
     * @return current table.
     * @param object entity to remove.
     */
    virtual MapTable &operator-=(MapObject &obj);
};

/**
 * The MapIndex allows linear access into a MapTable, that otherwise could have
 * its elements being retrieved only by key.
 * It can be increased, checked and dereferenced like a pointer, by means of
 * suitable operators.
 *
 * @author Sergio Repetto <s.repetto@pentaengineering.it>
 * @short Index object to access MapTable elements
 */
class __EXPORT MapIndex
{
    MapObject*  thisObject;

public :

    /**
     * Creates an empty map index (pointing to nothing).
     */
    MapIndex() : thisObject(NULL) {}

    /**
     * Creates a map index pointing to a specific map object
     *
     * @param the indexed object
     */
    MapIndex(MapObject* theObject) : thisObject(theObject) {}

    /**
     * Creates a copy of a given map index
     *
     * @param the source index object
     */
    MapIndex(const MapIndex& theIndex) : thisObject(theIndex.thisObject) {}

    /**
     * Dereference operator: the pointed object it is returned as void * for
     * easy re-cast.
     *
     * @return pointer to indexed object.
     */
    void* operator*() const {
        return (void*)thisObject;
    }

    /**
     * Assignment operator to avoid implicit cast.
     *
     * @return the object itself, as changed.
     */
    MapIndex& operator=(MapObject *theObject);

    /**
     * Prefix increment operator, to be used in loops and such.
     *
     * @return the object itself, as changed.
     */
    MapIndex& operator++();           // prefix

    /**
     * Postfix increment operator, to be used in loops and such.
     *
     * @return the object itself, as changed.
     */
    MapIndex  operator++(int) {     // postfix
        return this->operator++();
    }

    /**
     * Comparison operator, between two MapIndex's.
     *
     * @return the object itself, as changed.
     */
    bool operator==(const MapIndex& theIndex) const {
        return thisObject == theIndex.thisObject;
    }

    bool operator!=(const MapIndex& theIndex) const {
        return !(*this == theIndex);
    }

    /**
     * Comparison operator, between the MapIndex and a MapObject, useful to avoid
     * casts for sake of clearness.
     *
     * @return the object itself, as changed.
     */
    bool operator==(const MapObject* theObject) const {
        return thisObject == theObject;
    }

    bool operator!=(const MapObject* theObject) const {
        return !(*this == theObject);
    }
};

/**
 * The MapObject is a base class which can be used to make a derived
 * class operate on a MapTable.  Derived classes may override new and
 * delete operators to use managed free list from a MapTable.
 *
 * @author David Sugar <dyfet@gnutelephony.org>
 * @short Mappable object.
 */
class __EXPORT MapObject
{
private:
    __DELETE_COPY(MapObject);

protected:
    friend class MapTable;
    friend class MapIndex;
    MapObject *nextObject;
    const char *idObject;
    MapTable *table;

public:

    /**
     * Remove the object from it's current table.
     */
    void detach(void);

    /**
     * Save id, mark as not using any table.
     *
     * @param id string for this object.
     */
    MapObject(const char *id);
};

} // namespace ost

#endif
