// Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.

/**
 * A common object base class with auto-pointer support.
 * A common object class is used which may be referenced counted and
 * associated with a smart auto-pointer class.  A lot of the things
 * found here were inspired by working with Objective-C.  Many of the
 * classes are designed to offer automatic heap management through
 * smart pointers and temporary objects controlled through the scope of
 * the stack frame of method calls.
 * @file ucommon/object.h
 */

#ifndef _UCOMMON_OBJECT_H_
#define _UCOMMON_OBJECT_H_

#ifndef _UCOMMON_CPR_H_
#include <ucommon/cpr.h>
#endif

#ifndef _UCOMMON_GENERICS_H_
#include <ucommon/generics.h>
#endif

#ifndef _UCOMMON_PROTOCOLS_H_
#include <ucommon/protocols.h>
#endif

#include <stdlib.h>

namespace ucommon {

/**
 * A base class for reference counted objects.  Reference counted objects
 * keep track of how many objects refer to them and fall out of scope when
 * they are no longer being referred to.  This can be used to achieve
 * automatic heap management when used in conjunction with smart pointers.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT CountedObject : public ObjectProtocol
{
private:
    volatile unsigned count;

protected:
    /**
     * Construct a counted object, mark initially as unreferenced.
     */
    CountedObject();

    /**
     * Construct a copy of a counted object.  Our instance is not a
     * reference to the original object but a duplicate, so we do not
     * retain the original and we do reset our count to mark as
     * initially unreferenced.
     */
    CountedObject(const ObjectProtocol &ref);

    /**
     * Dealloc object no longer referenced.  The dealloc routine would commonly
     * be used for a self delete to return the object back to a heap when
     * it is no longer referenced.
     */
    virtual void dealloc(void);

    /**
     * Force reset of count.
     */
    inline void reset(void)
        {count = 0;}

public:
    /**
     * Test if the object has copied references.  This means that more than
     * one object has a reference to our object.
     * @return true if referenced by more than one object.
     */
    inline bool is_copied(void)
        {return count > 1;}

    /**
     * Test if the object has been referenced (retained) by anyone yet.
     * @return true if retained.
     */
    inline bool is_retained(void)
        {return count > 0;}

    /**
     * Return the number of active references (retentions) to our object.
     * @return number of references to our object.
     */
    inline unsigned copied(void)
        {return count;}

    /**
     * Increase reference count when retained.
     */
    void retain(void);

    /**
     * Decrease reference count when released.  If no longer retained, then
     * the object is dealloc'd.
     */
    void release(void);
};

/**
 * A general purpose smart pointer helper class.  This is particularly
 * useful in conjunction with reference counted objects which can be
 * managed and automatically removed from the heap when they are no longer
 * being referenced by a smart pointer.  The smart pointer itself would
 * normally be constructed and initialized as an auto variable in a method
 * call, and will dereference the object when the pointer falls out of scope.
 * This is actually a helper class for the typed pointer template.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT auto_object
{
protected:
    ObjectProtocol *object;

    auto_object();

public:
    /**
     * Construct an auto-pointer referencing an existing object.
     * @param object we point to.
     */
    auto_object(ObjectProtocol *object);

    /**
     * Construct an auto-pointer as a copy of another pointer.  The
     * retention of the object being pointed to will be increased.
     * @param pointer we are a copy of.
     */
    auto_object(const auto_object &pointer);

    /**
     * Delete auto pointer.  When it falls out of scope, the retention
     * of the object it references is reduced.  If it falls to zero in
     * a reference counted object, then the object is auto-deleted.
     */
    ~auto_object();

    /**
     * Manually release the pointer.  This reduces the retention level
     * of the object and resets the pointer to point to nobody.
     */
    void release(void);

    /**
     * Test if the pointer is not set.
     * @return true if the pointer is not referencing anything.
     */
    bool operator!() const;

    /**
     * Test if the pointer is referencing an object.
     * @return true if the pointer is currently referencing an object.
     */
    operator bool() const;

    /**
     * test if the object being referenced is the same as the object we specify.
     * @param object we compare to.
     * @return true if this is the object our pointer references.
     */
    bool operator==(ObjectProtocol *object) const;

    /**
     * test if the object being referenced is not the same as the object we specify.
     * @param object we compare to.
     * @return true if this is not the object our pointer references.
     */
    bool operator!=(ObjectProtocol *object) const;

    /**
     * Set our pointer to a specific object.  If the pointer currently
     * references another object, that object is released.  The pointer
     * references our new object and that new object is retained.
     * @param object to assign to.
     */
    void operator=(ObjectProtocol *object);
};

/**
 * A sparse array of managed objects.  This might be used as a simple
 * array class for reference counted objects.  This class assumes that
 * objects in the array exist when assigned, and that gaps in the array
 * are positions that do not reference any object.  Objects are automatically
 * created (create on access/modify when an array position is referenced
 * for the first time.  This is an abstract class because it is a type
 * factory for objects who's derived class form constructor is not known
 * in advance and is a helper class for the sarray template.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT SparseObjects
{
private:
    ObjectProtocol **vector;
    unsigned max;

protected:
    /**
     * Object factory for creating members of the spare array when they
     * are initially requested.
     * @return new object.
     */
    virtual ObjectProtocol *create(void) = 0;

    /**
     * Purge the array by deleting all created objects.
     */
    void purge(void);

    virtual ObjectProtocol *invalid(void) const;

    /**
     * Get (reference) an object at a specified offset in the array.
     * @param offset in array.
     * @return new or existing object.
     */
    ObjectProtocol *get(unsigned offset);

    /**
     * Create a sparse array of known size.  No member objects are
     * created until they are referenced.
     * @param size of array.
     */
    SparseObjects(unsigned size);

    /**
     * Destroy sparse array and delete all generated objects.
     */
    virtual ~SparseObjects();

public:
    /**
     * Get count of array elements.
     * @return array elements.
     */
    unsigned count(void);
};

/**
 * Generate a typed sparse managed object array.  Members in the array
 * are created when they are first referenced.  The types for objects
 * that are generated by sarray must have Object as a base class.  Managed
 * sparse arrays differ from standard arrays in that the member elements
 * are not allocated from the heap when the array is created, but rather
 * as they are needed.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <class T>
class sarray : public SparseObjects
{
public:
    /**
     * Generate a sparse typed array of specified size.
     * @param size of array to create.
     */
    inline sarray(unsigned size) : SparseObjects(size) {}

    /**
     * Get typed member of array.  If the object does not exist, it is
     * created.
     * @param offset in array for object.
     * @return pointer to typed object.
     */
    inline T *get(unsigned offset)
        {return static_cast<T*>(SparseObjects::get(offset));}

    /**
     * Array operation to access member object.  If the object does not
     * exist, it is created.
     * @param offset in array for object.
     * @return pointer to typed object.
     */
    inline T& operator[](unsigned offset)
        {return get(offset);}

    inline const T* at(unsigned offset)
        {return static_cast<const T&>(SparseObjects::get(offset));}

private:
    __LOCAL ObjectProtocol *create(void)
        {return new T;}
};

/**
 * Template for embedding a data structure into a reference counted object.
 * This is a convenient means to create reference counted heap managed data
 * structure.  This template can be used for embedding data into other kinds
 * of managed object classes in addition to reference counting.  For example,
 * it can be used to embed a data structure into a linked list, as shown in
 * the linked_value template.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <typename T, class O = CountedObject>
class object_value : public O
{
protected:
    /**
     * Assign our value from a typed data object.  This is a helper method.
     * @param object to assign our value from.
     */
    inline void set(const T& object)
        {value = object;}

public:
    T value;    /**< Embedded data value */

    /**
     * Construct composite value object.
     */
    inline object_value() : O() {}

    /**
     * Construct composite value object and assign from existing data value.
     * @param existing typed value to assign.
     */
    inline object_value(T& existing) : O()
        {value = existing;}

    /**
     * Pointer reference to embedded data value.
     * @return embedded value.
     */
    inline T& operator*()
        {return value;}

    /**
     * Assign embedded data value.
     * @param data value to assign.
     */
    inline void operator=(const T& data)
        {value = data;}

    /**
     * Retrieve data value by casting reference.
     * @return embedded value.
     */
    inline operator T&()
        {return value;}

    inline T& operator()()
        {return value;}

    /**
     * Set data value by expression reference.
     * @param data value to assign.
     */
    inline void operator()(T& data)
        {value = data;}
};

/**
 * Typed smart pointer class.  This is used to manage references to
 * a specific typed object on the heap that is derived from the base Object
 * class.  This is most commonly used to manage references to reference
 * counted heap objects so their heap usage can be auto-managed while there
 * is active references to such objects.  Pointers are usually created on
 * the stack frame and used to reference an object during the life of a
 * member function.  They can be created in other objects that live on the
 * heap and can be used to maintain active references so long as the object
 * they are contained in remains in scope as well.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <class T, class P = auto_object>
class object_pointer : public P
{
public:
    /**
     * Create a pointer with no reference.
     */
    inline object_pointer() : P() {}

    /**
     * Create a pointer with a reference to a heap object.
     * @param object we are referencing.
     */
    inline object_pointer(T* object) : P(object) {}

    /**
     * Reference object we are pointing to through pointer indirection.
     * @return pointer to object we are pointing to.
     */
    inline T* operator*() const
        {return static_cast<T*>(P::object);}

    /**
     * Reference object we are pointing to through function reference.
     * @return object we are pointing to.
     */
    inline T& operator()() const
        {return *(static_cast<T*>(P::object));}

    /**
     * Reference member of object we are pointing to.
     * @return reference to member of pointed object.
     */
    inline T* operator->() const
        {return static_cast<T*>(P::object);}

    /**
     * Get pointer to object.
     * @return pointer or NULL if we are not referencing an object.
     */
    inline T* get(void) const
        {return static_cast<T*>(P::object);}

    /**
     * Iterate our pointer if we reference an array on the heap.
     * @return next object in array.
     */
    inline T* operator++()
        {P::operator++(); return get();}

    /**
     * Iterate our pointer if we reference an array on the heap.
     * @return previous object in array.
     */
    inline void operator--()
        {P::operator--(); return get();}

    /**
     * Perform assignment operator to existing object.
     * @param typed object to assign.
     */
    inline void operator=(T *typed)
        {P::operator=((ObjectProtocol *)typed);}

    /**
     * See if pointer is set.
     */
    inline operator bool()
        {return P::object != NULL;}

    /**
     * See if pointer is not set.
     */
    inline bool operator!()
        {return P::object == NULL;}
};

/**
 * Convenience function to access object retention.
 * @param object we are retaining.
 */
inline void retain(ObjectProtocol *object)
    {object->retain();}

/**
 * Convenience function to access object release.
 * @param object we are releasing.
 */
inline void release(ObjectProtocol *object)
    {object->release();}

/**
 * Convenience function to access object copy.
 * @param object we are copying.
 */
inline ObjectProtocol *copy(ObjectProtocol *object)
    {return object->copy();}

} // namespace ucommon

#endif
