/** 
 * @file    kQueue.h
 * @brief   Declares the kQueue class. 
 *
 * @internal
 * Copyright (C) 2005-2022 by LMI Technologies Inc.
 * Licensed under the MIT License.
 * Redistributed files must retain the above copyright notice.
 */
#ifndef K_API_QUEUE_H
#define K_API_QUEUE_H

#include <kApi/kApiDef.h>
#include <kApi/Data/kQueue.x.h>

/**
 * @class       kQueue
 * @extends     kObject
 * @implements  kCollection
 * @ingroup     kApi-Data
 * @brief       Represents a FIFO queue implemented with a dynamic array.
 * 
 * The kQueue class represents a dynamic, array-based queue of objects or values. The kQueue constructor 
 * accepts a kType value that determines the type of items that will be stored in the queue. The queue will 
 * automatically grow as new items are added. 
 * 
 * @code {.c}
 * kStatus QueueExample()
 * {
 *     kQueue queue = kNULL; 
 *     k32s values[] = { 1, 2, 3, 5, 7, 9 }; 
 *     kSize i; 
 *     
 *     kTry
 *     {
 *          //create a queue that can store 32-bit integers
 *          kTest(kQueue_Construct(&queue, kTypeOf(k32s), 0, kNULL)); 
 *          
 *          //add some initial items to the queue
 *          for (i = 0; i < kCountOf(values); ++i)
 *          {
 *              kTest(kQueue_Add(queue, &values[i]); 
 *          }
 *          
 *          //print some information about the queue and its items
 *          printf("Item type: %s\n", kType_Name(kQueue_ItemType(queue))); 
 *          printf("Count: %u\n", (k32u) kQueue_Count(queue)); 
 *  
 *          for (i = 0; i < kQueue_Count(queue); ++i)
 *          {
 *              //the kQueue_As_ macro can be used to get a queue item and cast it to the desired type; 
 *              //this is equivalent to *(k32s*)kQueue_At(queue, i);
 *              k32s value = kQueue_As_(queue, i, k32s); 
 *
 *              printf("Item %u: %d\n", (k32u)i, value);  
 *          }
 *     }
 *     kFinally
 *     {
 *          kObject_Destroy(queue); 
 * 
 *          kEndFinally(); 
 *     }
 *    
 *     return kOK; 
 * }
 * 
 * @endcode
 *
 * For queues that contain <em>objects</em> (e.g. kImage) as opposed to <em>values</em> (e.g. k32s), the objects 
 * are not automatically destroyed when the queue is destroyed. To recursively destroy both the queue and the 
 * queue items, use kObject_Dispose.  
 *
 * kQueue supports the kObject_Clone, kObject_Dispose, and kObject_Size methods.
 * 
 * kQueue supports the kdat5 and kdat6 serialization protocols.
 */
//typedef kObject kQueue;       --forward-declared in kApiDef.x.h 

/** 
 * Constructs a kQueue object.
 *
 * @public                      @memberof kQueue
 * @param   queue               Receives constructed queue object. 
 * @param   itemType            Type of queue element.
 * @param   initialCapacity     Capacity initially reserved for queue items. 
 * @param   allocator           Memory allocator (or kNULL for default). 
 * @return                      Operation status. 
 */
kFx(kStatus) kQueue_Construct(kQueue* queue, kType itemType, kSize initialCapacity, kAlloc allocator); 

/** 
 * Reallocates the queue item buffer.
 *
 * @public                      @memberof kQueue
 * @param   queue               Queue object. 
 * @param   itemType            Type of queue element.
 * @param   initialCapacity     Capacity initially reserved for queue items. 
 * @return                      Operation status. 
 */
kFx(kStatus) kQueue_Allocate(kQueue queue, kType itemType, kSize initialCapacity); 

/** 
 * Performs a shallow copy of the source queue.  
 *
 * Source items are copied by value; if the source queue contains objects, the object 
 * handles are copied but the objects are not cloned. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   source      Source queue to be copied.
 * @return              Operation status. 
 */
kFx(kStatus) kQueue_Assign(kQueue queue, kQueue source); 

/** 
 * Sets the count of queue items to zero. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @return              Operation status. 
 */
kInlineFx(kStatus) kQueue_Clear(kQueue queue)
{
    kObj(kQueue, queue); 

    obj->count = 0; 
    obj->head = 0; 

    return kOK; 
}

/** 
 * Disposes any elements in the queue and sets the count of queue items to zero.
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @return              Operation status. 
 */
kFx(kStatus) kQueue_Purge(kQueue queue); 

/** 
 * Ensures that capacity is reserved for at least the specified number of queue items. 
 *
 * Existing queue items are preserved. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   capacity    Queue capacity, in items.
 * @return              Operation status. 
 */
kFx(kStatus) kQueue_Reserve(kQueue queue, kSize capacity); 

/** 
 * Adds the specified item to the end of the queue.
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   item        Pointer to item that will be copied (by value) into the queue.
 * @return              Operation status. 
 */
kFx(kStatus) kQueue_Add(kQueue queue, const void* item); 

/** 
 * Adds the specified item to the end of the queue.
 *
 * A debug assertion will be raised if the size of the specified item type is not equal to the 
 * size of the collection item type.
 * 
 * @relates                 kQueue
 * @param   kQueue_queue    Queue object. 
 * @param   TPtr_item       Strongly-typed pointer to item that will be copied (by value) into the queue.
 * @return                  Operation status. 
 */
#define kQueue_AddT(kQueue_queue, TPtr_item) \
    xkQueue_AddT(kQueue_queue, TPtr_item, sizeof(*TPtr_item))

/** 
 * Removes the item at the head of the queue. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   item        Destination for the removed item (copied by value, can be null).
 * @return              Operation status. 
 */
kFx(kStatus) kQueue_Remove(kQueue queue, void* item);

/** 
 * Sets the value of an item.
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   index       Item index. 
 * @param   item        Pointer to item that will be copied into the queue.
 * @return              Operation status. 
 */
kFx(kStatus) kQueue_SetItem(kQueue queue, kSize index, const void* item); 

/** 
 * Sets the value of an item.
 *
 * A debug assertion will be raised if the size of the specified item type is not equal to the 
 * size of the collection item type.
 * 
 * @relates                 kQueue
 * @param   kQueue_queue    Queue object. 
 * @param   kSize_index     Item index. 
 * @param   TPtr_item       Strongly-typed pointer to item that will be copied into the queue.
 * @return                  Operation status. 
 */
#define kQueue_SetItemT(kQueue_queue, kSize_index, TPtr_item) \
    xkQueue_SetItemT(kQueue_queue, kSize_index, TPtr_item, sizeof(*TPtr_item))

/** 
 * Gets the value of an item. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   index       Item index.  
 * @param   item        Destination for item that will be copied (by value) from the queue. 
 * @return              Operation status. 
 */
kFx(kStatus) kQueue_Item(kQueue queue, kSize index, void* item);  

/** 
 * Gets the value of an item. 
 *
 * A debug assertion will be raised if the size of the specified item type is not equal to the 
 * size of the collection item type.
 * 
 * @relates                 kQueue
 * @param   kQueue_queue    Queue object. 
 * @param   kSize_index     Item index.  
 * @param   TPtr_item       Strongly-typed destination pointer for item that will be copied (by value) from the queue. 
 * @return                  Operation status. 
 */
#define kQueue_ItemT(kQueue_queue, kSize_index, TPtr_item) \
     xkQueue_ItemT(kQueue_queue, kSize_index, TPtr_item, sizeof(*TPtr_item))

/** 
* Sets the value of an item. 
* 
* A debug assertion will be raised if the size of the specified item type is not equal to the 
* size of the collection item type.
*
* @relates                  kQueue
* @param   kQueue_queue     Queue object. 
* @param   kSize_index      Item index.  
* @param   T_value          Item value.  
* @param   T                Item type identifier (e.g., k32s).
* @return                   Item value. 
*/
#define kQueue_SetAsT(kQueue_queue, kSize_index, T_value, T) \
    (kPointer_WriteAs(xkQueue_AsT(kQueue_queue, kSize_index, sizeof(T)), T_value, T), (void)0)

/** 
* Gets the value of an item. 
* 
* A debug assertion will be raised if the size of the specified item type is not equal to the 
* size of the collection item type.
*
* @relates                  kQueue
* @param   kQueue_queue     Queue object. 
* @param   kSize_index      Item index.  
* @param   T                Item type identifier (e.g., k32s).
* @return                   Item value. 
*/
#define kQueue_AsT(kQueue_queue, kSize_index, T) \
    kPointer_ReadAs(xkQueue_AsT(kQueue_queue, kSize_index, sizeof(T)), T)

/** 
 * Increases the queue count by the specified amount.
 *
 * Increases queue capacity if necessary; existing queue items are preserved. 
 * New items are not initialized. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   count       Amount to add to the existing count.
 * @return              Operation status. 
 */
kInlineFx(kStatus) kQueue_AddCount(kQueue queue, kSize count)
{
    kObj(kQueue, queue); 

    if ((obj->count + count) > obj->capacity)
    {
        kCheck(kQueue_Reserve(queue, obj->count + count)); 
    }

    obj->count += count; 

    return kOK; 
}

/** 
 * Decreases the queue count by the specified amount.
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   count       Amount to remove from the existing count.
 * @return              Operation status. 
 */
kInlineFx(kStatus) kQueue_RemoveCount(kQueue queue, kSize count)
{
    kObj(kQueue, queue); 

    if (count > obj->count)
    {
        return kERROR_STATE; 
    }

    obj->count -= count; 
    obj->head += count; 

    if (obj->head >= obj->capacity)
    {
        obj->head -= obj->capacity; 
    }

    return kOK; 
}

/** 
 * Calculates an element address.
 *
 * This method is similar to the At method, but does not bounds-check the index argument. 
 * 
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   index       Item index.  
 * @return              Calculated pointer.
 */
kInlineFx(void*) kQueue_DataAt(kQueue queue, kSize index)
{
    kObj(kQueue, queue); 
    
    if ((obj->head + index) >= obj->capacity)
    {
        return kPointer_ItemOffset(obj->items, (kSSize)(obj->head + index - obj->capacity), obj->itemSize); 
    }
    else
    {
        return kPointer_ItemOffset(obj->items, (kSSize)(obj->head + index), obj->itemSize);
    }
}

/** 
 * Calculates an element address.
 *
 * This method is similar to the At method, but does not bounds-check the index argument. 
 * 
 * A debug assertion will be raised if the size of the specified item type is not equal to the 
 * size of the collection item type.
 * 
 * @relates                 kQueue
 * @param   kQueue_queue    Queue object. 
 * @param   kSize_index     Item index.  
 * @param   T               Item type identifier (e.g., k32s).
 * @return                  Calculated pointer.
 */
#define kQueue_DataAtT(kQueue_queue, kSize_index, T) \
    kCast(T*, xkQueue_DataAtT(kQueue_queue, kSize_index, sizeof(T)))

/** 
 * Returns a pointer to the specified item in the queue buffer. 
 *
 * A debug assertion will be raised if the index argument is greater than or equal to 
 * the current queue count (note: count, not capacity).  
 * 
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @param   index       Item index.  
 * @return              Pointer to queue element.
 */
kInlineFx(void*) kQueue_At(kQueue queue, kSize index)
{    
#   if !defined(K_FSS_912_DISABLE_BOUNDS_CHECK)
    {
        kAssert(index < kQueue_Count(queue));
    }
#   endif

    return kQueue_DataAt(queue, index);
}

/** 
 * Returns a strongly-typed pointer to the specified item in the queue buffer. 
 *
 * A debug assertion will be raised if the index argument is greater than or equal to 
 * the current queue count (note: count, not capacity), or if the size of the specified 
 * item type is not equal to the size of the collection item type.
 * 
 * @relates                 kQueue
 * @param   kQueue_queue    Queue object. 
 * @param   kSize_index     Item index.  
 * @param   T               Item type identifier (e.g., k32s).
 * @return                  Strongly-typed pointer to queue element.
 */
#define kQueue_AtT(kQueue_queue, kSize_index, T) \
    kCast(T*, xkQueue_AtT(kQueue_queue, kSize_index, sizeof(T)))

/** 
 * Returns the queue element type. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @return              Item type. 
 */
kInlineFx(kType) kQueue_ItemType(kQueue queue)
{
    kObj(kQueue, queue); 

    return obj->itemType;
}

/** 
 * Returns the queue element size.
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @return              Item size, in bytes. 
 */
kInlineFx(kSize) kQueue_ItemSize(kQueue queue)
{
    kObj(kQueue, queue); 

    return obj->itemSize;
}

/** 
 * Returns the current count of items in the queue. 
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @return              Current count of items. 
 */
kInlineFx(kSize) kQueue_Count(kQueue queue)
{
    kObj(kQueue, queue); 

    return obj->count; 
}

/** 
 * Returns the number of elements for which space has been allocated.  
 *
 * @public              @memberof kQueue
 * @param   queue       Queue object. 
 * @return              Queue capacity. 
 */
kInlineFx(kSize) kQueue_Capacity(kQueue queue)
{
    kObj(kQueue, queue); 

    return obj->capacity;
} 

#endif
