/** 
 * @file    kObject.h
 * @brief   Declares the kObject class. 
 *
 * @internal
 * Copyright (C) 2005-2022 by LMI Technologies Inc.
 * Licensed under the MIT License.
 * Redistributed files must retain the above copyright notice.
 */
#include <kApi/kApiDef.h>   //--inclusion order controlled by kApiDef

#ifndef K_API_OBJECT_H
#define K_API_OBJECT_H

#include <kApi/kObject.x.h>

/**
 * @class   kObject
 * @ingroup kApi
 * @brief   Root of all Zen classes. 
 * 
 * The kObject class provides infrastructure to support object destruction, reference counting, and type introspection. 
 *
 * The kObject_Destroy method can be used to destroy an instance of any class. The kObject_Dispose method provides 
 * support for recursive destruction, often used in conjunction with Zen data collections. Refer to @ref kApi-Destruction 
 * for more information. 
 * 
 * The kObject_Clone method can be used to create a deep copy of an object. The implementation of this method 
 * requires additional support from derived types; refer to the documentation for a specific derived type to determine 
 * whether cloning is supported. 
 * 
 * The kObject_Clone and kObject_Dispose methods are often used together to manage collections of data objects: 
 * 
 * @code {.c}
 * 
 * kList list = kNULL; 
 * kList copy = kNULL; 
 * kImage image = kNULL;
 * kSize imageCount = 10; 
 *
 * //create a list of image objects
 * kCheck(kList_Construct(&list, kTypeOf(kImage), imageCount, kNULL)); 
 * 
 * for (kSize i = 0; i < imageCount; ++i)
 * {
 *     kCheck(kImage_Construct(&image, kTypeOf(kArgb), 640, 480, kNULL)); 
 *     kList_Add(list, &image); 
 * }
 * 
 * //make a deep copy of the list; copies both the list and the images contained in the list
 * kCheck(kObject_Clone(&copy, list, kNULL)); 
 * //...
 * 
 * //clean up; destroy the lists and the images contained in the lists
 * kObject_Dispose(list); 
 * kObject_Dispose(copy);
 *
 * @endcode
 *
 * All kObject-derived instances are reference counted. The kObject_Share method can optionally be used to increment an object's 
 * reference count, while the kObject_Destroy/kObject_Dispose methods are always used to decrement an object's reference 
 * count. Refer to @ref kApi-Reference-Counting for more information.
 *
 * The kObject_Type method can be used to access type information for any kObject instance. The kType object returned
 * by this method can be used to learn about the object's class, including its name, base classes, implemented interfaces, 
 * and methods.  The kObject_Is method provides a convenient way to determine whether an object derives from a specific 
 * base class or implements an interface. 
 * 
 * The kObject_Equals and kObject_HashCode methods can be helpful for object comparisons, but require support from 
 * derived types and are infrequently overridden. The kString class overrides both of these methods to support the use of 
 * kString objects as hash keys in kMap data collections. 
 */
//typedef kPointer kObject;   --forward-declared in kApiDef.x.h

/*
* Public 
*/

/** 
 * Constructs a new object by copying an existing object, including any aggregated child elements. 
 *
 * If the source object is an object collection (e.g. kArrayList<kString>), any aggregated child objects 
 * are also cloned. In this case, the kObject_Dispose method can be used to free the cloned collection and 
 * its associated elements. 
 * 
 * This method will fail if the source object (or an aggregated child element) does not support cloning.
 * 
 * To support cloning, derived classes should override the kObject_VInitClone method. 
 * 
 * @public              @memberof kObject
 * @param   object      Receives the constructed object. 
 * @param   source      Source object. 
 * @param   allocator   Memory allocator (or kNULL for default). 
 * @return              Operation status. 
 * @see                 kObject_Dispose
 */
kInlineFx(kStatus) kObject_Clone(kObject* object, kObject source, kAlloc allocator)
{
    if (!kIsNull(source))
    {
        return xkObject_CloneImpl(object, source, allocator);
    }
    
    *object = kNULL;
    
    return kOK;  
}

/** 
 * Increments the reference count associated with this object. 
 *
 * This method is thread-safe.
 *
 * @public              @memberof kObject
 * @param   object      Object. 
 * @return              Operation status. 
 * @see                 reference-counting
 */
kInlineFx(kStatus) kObject_Share(kObject object)
{
    kObj(kObject, object);

    kAtomic32s_Increment(&obj->refCount); 
    
    return kOK; 
}

/** 
 * Sets the object pool associated with this object. 
 *
 * Object pools can be used to implement custom lifetime management. If an object has an assigned pool, 
 * then the kObjectPool_Reclaim method will be called just prior to destruction, to provide an opportunity
 * for the object to be reclaimed. 
 *
 * @public              @memberof kObject
 * @param   object      Object. 
 * @param   pool        Pool object (or kNULL to clear the pool assignment). 
 * @return              Operation status. 
 */
kInlineFx(kStatus) kObject_SetPool(kObject object, kObjectPool pool)
{
    kObj(kObject, object);

    obj->pool = pool; 

    return kOK; 
}

/** 
 * Destroys the object. 
 * 
 * The kObject_Destroy method destroys the object itself and any resources that are owned by the object.  
 * See @ref kApi-Destruction for more information. 
 * 
 * When an object is destroyed (or disposed), its reference count is decremented. The object is only truly 
 * destroyed when the reference count reaches zero. See @ref kApi-Reference-Counting for more information.
 * 
 * To support releasing resources, derived classes should override the kObject_VRelease method. 
 * 
 * @public              @memberof kObject
 * @param   object      Object (or kNULL). 
 * @return              Operation status. 
 * @see                 @ref kApi-Destruction, @ref kApi-Reference-Counting
 */
kInlineFx(kStatus) kObject_Destroy(kObject object)
{
    if (!kIsNull(object))
    {
        return xkObject_DestroyImpl(object, kFALSE);
    }
    
    return kOK; 
}

/** 
 * Destroys the object and any aggregated child elements. 
 * 
 * The kObject_Dispose method destroys the object itself, any resources that are owned by the object, and 
 * if the object represents a collection of objects, any child objects in the collection. See @ref kApi-Destruction 
 * for more information. 
 * 
 * When an object is destroyed (or disposed), its reference count is decremented. The object is only truly 
 * destroyed when the reference count reaches zero. See @ref kApi-Reference-Counting for more information.
 * 
 * To support destroying aggregated child elements during kObject_Dispose, derived classes should override the 
 * kObject_VDisposeItems method. 
 * 
 * @public              @memberof kObject
 * @param   object      Object (or kNULL). 
 * @return              Operation status. 
 * @see                 @ref kApi-Destruction, @ref kApi-Reference-Counting
 */
kInlineFx(kStatus) kObject_Dispose(kObject object)
{
    if (!kIsNull(object))
    {
        return xkObject_DestroyImpl(object, kTRUE);
    }
    
    return kOK; 
}

/**
 * Returns the type of the object.
 *
 * Each object is an instance of a specific class type. The type handle returned by this function can be 
 * used to learn about the class. 
 * 
 * @public              @memberof kObject
 * @param   object      Object. 
 * @return              Type. 
 * @see                 kType
 */      
kInlineFx(kType) kObject_Type(kObject object)
{
    kObj(kObject, object);

    return obj->type; 
}

/**
 * Determines whether this object is an instance of the specified type.
 * 
 * This function compares the type of this object with the given type. An object is considered to be an 
 * instance of a given type if a) the type represents a class and this object inherits from (or instantiates) 
 * that class, or b) the type represents an interface and this object implements the interface. 
 *
 * @public              @memberof kObject
 * @param   object      Object. 
 * @param   type        Type. 
 * @return              kTRUE if the object is of the specified type; otherwise kFALSE. 
 * @see                 kType_Is
 */
kInlineFx(kBool) kObject_Is(kObject object, kType type)
{
    kObjR(kObject, object);

    return !kIsNull(object) && !kIsNull(type) && !kIsNull(obj->type) &&
           xkObject_RawVerifyTag(object) && kType_Is(obj->type, type); 
}

/**
 * Determines whether the object is equal to another object. 
 * 
 * By default, objects are compared by reference; objects are considered equal if the given handles refer to 
 * the same object instance. The kObject_VEquals method can optionally be overridden to provide a more meaningful 
 * equality comparison. 
 * 
 * @public              @memberof kObject
 * @param   object      Object. 
 * @param   other       Object for comparison. 
 * @return              kTRUE if the objects are equal.
 */
kInlineFx(kBool) kObject_Equals(kObject object, kObject other)
{
    return xkObject_VTable(object)->VEquals(object, other); 
}

/**
 * Gets a hash code representing the state of this object.
 * 
 * By default, objects return a hash code based on the object handle value. The kObject_VHashCode method can optionally 
 * be overridden to provide a more useful hash. 
 *
 * @public              @memberof kObject
 * @param   object      Object. 
 * @return              Hash code. 
 */
kInlineFx(kSize) kObject_HashCode(kObject object)
{
    return xkObject_VTable(object)->VHashCode(object); 
}

/**
 * Gets the memory allocator associated with this object.  
 * 
 * Most objects are constructed with an allocator, which is used to allocate the memory required by the 
 * object. Objects retain a reference to this allocator to enable further allocations and to free memory when 
 * the object is destroyed. 
 *
 * @public              @memberof kObject
 * @param   object      Object. 
 * @return              Memory allocator. 
 * @see                 kAlloc
*/
kInlineFx(kAlloc) kObject_Alloc(kObject object)
{
    kObj(kObject, object);

    return obj->alloc;
}

/** 
 * Reports whether the object, including aggregated child elements, contains any foreign memory references.   
 * 
 * @public              @memberof kObject
 * @param   object      Object. 
 * @return              kTRUE if the object references memory allocated in a foreign address space. 
 */
kInlineFx(kBool) kObject_HasForeignData(kObject object)
{
    return xkObject_VTable(object)->VHasForeignData(object);
}

/**
 * Estimates the memory consumed by this object, including any aggregated child elements. 
 * 
 * The default implementation of this method reports only the size of the class instance (additional allocations 
 * performed by the class are excluded). Derived classes can override kObject_VSize to provide type-specific 
 * size estimates. 
 *
 * @public              @memberof kObject
 * @param   object      Object. 
 * @return              Object size, in bytes.  
*/
kInlineFx(kSize) kObject_Size(kObject object)
{    
    return xkObject_VTable(object)->VSize(object);
}

/**
 * Reports whether the object is currently shared (reference count greater than one). 
 * 
 * Objects are initialized with a reference count of one. The kObject_Share method can be used to increment 
 * the reference count. The kObject_Destroy and kObject_Dispose methods decrease the reference count, and 
 * when the reference count reaches zero, the object is actually destroyed/disposed. 
 * 
 * This method can be used to determine if the reference count of an object is currently greater than one. 
 * 
 * This method is thread-safe. 
 *
 * @public              @memberof kObject
 * @param   object      Object. 
 * @return              kTRUE if the object is shared; kFALSE otherwise. 
 * @see                 @ref kApi-Reference-Counting, kObject_Share
*/
kInlineFx(kBool) kObject_IsShared(kObject object)
{
    kObj(kObject, object);

    return kAtomic32s_Get(&obj->refCount) > 1; 
}

/*
* Protected 
*/

/**
 * Protected method called by derived classes to initialize the kObject base class. 
 * 
 * This method should typically be called as the first statement in derived initializer methods. 
 *
 * @protected           @memberof kObject
 * @param   object      Object instance (not yet initialized). 
 * @param   type        Object type (required).
 * @param   alloc       Memory allocator (required).
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_Init(kObject object, kType type, kAlloc alloc) 
{ 
    kObjR(kObject, object);

    obj->type = type; 
    obj->alloc = alloc; 
    obj->pool = kNULL; 
    obj->tag = xkOBJECT_TAG; 

    kAtomic32s_Init(&obj->refCount, 1);

    return kOK; 
}

/**
 * Protected method called by derived classes to allocate memory using the object's allocator. 
 * 
 * This method is provided for convenience; alternatively, the allocator provided by kObject_Alloc can also be used directly.
 *
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @param   size        Size of memory to allocate, in bytes.
 * @param   mem         Receives pointer to allocated memory (pointer to a pointer).
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_GetMem(kObject object, kSize size, void* mem)
{
    return kAlloc_Get(kObject_Alloc(object), size, mem);
}

/**
 * Protected method called by derived classes to allocate and zero memory using the object's allocator. 
 * 
 * This method is provided for convenience; alternatively, the allocator provided by kObject_Alloc can also be used directly.
 *
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @param   size        Size of memory to allocate, in bytes.
 * @param   mem         Receives pointer to allocated memory (pointer to a pointer).
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_GetMemZero(kObject object, kSize size, void* mem)
{
    return kAlloc_GetZero(kObject_Alloc(object), size, mem); 
}

/**
 * Protected method called by derived classes to free memory using the object's allocator. 
 * 
 * This method is provided for convenience; alternatively, the allocator provided by kObject_Alloc can also be used directly.
 *
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @param   mem         Pointer to memory to free (or kNULL). 
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_FreeMem(kObject object, void* mem)
{
    return kAlloc_Free(kObject_Alloc(object), mem);
}

/**
 * Protected method called by derived classes to free memory (and reset the provided memory pointer to kNULL) using the object's allocator. 
 * 
 * This method is provided for convenience; alternatively, the allocator provided by kObject_Alloc can also be used directly.
 *
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @param   mem         Pointer to pointer to memory to free (or pointer to kNULL). 
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_FreeMemRef(kObject object, void* mem)
{
    return kAlloc_FreeRef(kObject_Alloc(object), mem);
}

/**
 * Protected virtual method that deallocates any resources owned by the object. 
 * 
 * This method can be overridden in derived classes to free any resources owned by 
 * derived instances. 
 * 
 * Each derived class should call the VRelease method of its parent class as the 
 * last statement in the derived VRelease method. 
 * 
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_VRelease(kObject object)                  
{
    kObjR(kObject, object);

    obj->tag = 0;
    
    return kOK;
}

/**
 * Protected virtual method that clones (makes a deep copy of) the specified source object.
 * 
 * This method can be overridden in derived classes to support kObject_Clone.   
 * 
 * There is no requirement to call kObject_VInitClone from derived VInitClone methods.
 *
 * @protected           @memberof kObject
 * @param   object      Object instance (not yet initialized). 
 * @param   source      Object to be cloned (required).
 * @param   allocator   Memory allocator (required).
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_VInitClone(kObject object, kObject source, kAlloc allocator)
{
    return kERROR_UNIMPLEMENTED; 
}

/**
 * Protected virtual method that destroys any aggregated child objects associated with a collection.
 * 
 * This method can be overridden in derived collection classes to support kObject_Dispose.
 * 
 * There is no requirement to call kObject_VDisposeItems from derived VDisposeItems methods.
 *
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @return              Operation status.  
*/
kInlineFx(kStatus) kObject_VDisposeItems(kObject object)
{
    return kOK;
}

/** 
 * Protected virtual method that reports whether the object, including aggregated child elements, contains any foreign memory references.   
 * 
 * @protected           @memberof kObject
 * @param   object      Object. 
 * @return              kTRUE if the object references memory allocated in a foreign address space. 
 */
kInlineFx(kBool) kObject_VHasForeignData(kObject object)
{
    return kFALSE; 
}

/**
 * Protected virtual method that calculates the total size (in bytes) of the object instance.
 * 
 * This method can be overridden in derived collection classes to support kObject_Size.
 * 
 * Collection classes should report the size of any aggregated child elements. 
 * 
 * There is no requirement to call kObject_VSize from derived VSize methods. However, derived
 * classes have the option of doing so, if it simplifies implementation.
 *
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @return              Operation status.  
*/
kInlineFx(kSize) kObject_VSize(kObject object)
{
    return kType_InnerSize(kObject_Type(object)); 
}

/**
 * Protected virtual method that calculates a hash code representing the object instance. 
 * 
 * This method can be overridden in derived collection classes to support kObject_HashCode.
 * 
 * The purpose of this method is to generate a hash code that can be used with hash-based 
 * collections such as kMap. 
 *
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @return              Hash code. 
*/
kInlineFx(kSize) kObject_VHashCode(kObject object)
{
    return xkHashPointer(object); 
}

/**
 * Protected virtual method that compares two objects for equality. 
 * 
 * This method can be overridden in derived collection classes to support kObject_Equals.
 * 
 * The default implementation of this method compares objects according to pointer/handle 
 * equality. In many cases, this is adequate/appropriate. This method can be overidden 
 * to implement a more specialized/type-specific representation of equality. 
 * 
 * @protected           @memberof kObject
 * @param   object      Object instance. 
 * @param   other       Object for comparison. 
 * @return              kTRUE if the objects are equal.
*/
kInlineFx(kBool) kObject_VEquals(kObject object, kObject other)
{
    return (object == other); 
}

#endif 
