/*
  PRIQUEUE.h  -  simple heap based priority queue

  Copyright (C)         Lumiera.org
    2011,               Christian Thaeter <ct@pipapo.org>

  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 General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/** @file priqueue.h
 ** Simple priority queue implementation based on a binary heap.
 ** Only 'insert' 'remove' and 'peek' operations are supported.
 ** Memory is dynamically managed through an optionally user supplied 'resize' function.
 ** Elements in the queue have a user-define size but should kept as small as possible.
 ** This is only intended to associate lightweight data such as a key and a pointer,
 ** storing the key in the element can save dereferencing cost and thus improve
 ** cache locality.
 ** 
 ** @warning elements in the queue get moved in memory, so referencing them is not allowed.
 ** 
 ** @todo we might add operations to change the priority of an arbitrary element or remove
 **       any but the topmost element. The idea is to let expired elements sink to the top
 **       and just detect and remove them on next access.
 ** 
 ** @see backend::engine::SchedulerFrontend
 ** 
 */


#ifndef LIB_PRIQUEUE_H
#define LIB_PRIQUEUE_H


#include "lib/error.h"


typedef struct lumiera_priqueue_struct lumiera_priqueue;
typedef lumiera_priqueue* LumieraPriQueue;

/** function to compare 2 keys, mandatory */
typedef int (*lumiera_priqueue_cmp_fn)(void*, void*);

/** function to copy elements, optional.
 *  Has the same prototype as memcpy which is used by default */
typedef void *(*lumiera_priqueue_copy_fn)(void *dest, const void *src, size_t n);

/** called when used hits the high or low water marks and initially by priqueue_init() (with queue==NULL)
 *  or at priqueue_destroy (with queue != NULL, used elements == 0), optional.
 *  
 * @note must be aware of resizes by more than just incrementing the queue by one
 */
typedef LumieraPriQueue (*lumiera_priqueue_resize_fn) (LumieraPriQueue);


/**
 * @remarks this structure is not opaque to make it possible
 *          to implement a low level resize operation
 *          which has to reallocate the queue and update
 *          the high and low water marks.
 */
struct lumiera_priqueue_struct
{
  void* queue;
  size_t element_size;
  unsigned used;
  unsigned high_water;                          ///< elements in the queue
  unsigned low_water;                           ///< size for shrinking the queue

  lumiera_priqueue_cmp_fn cmpfn;
  lumiera_priqueue_copy_fn copyfn;

  lumiera_priqueue_resize_fn resizefn;
};






LumieraPriQueue
lumiera_priqueue_init (LumieraPriQueue self,
                       size_t element_size,
                       lumiera_priqueue_cmp_fn cmpfn,
                       lumiera_priqueue_copy_fn copyfn,
                       lumiera_priqueue_resize_fn resizefn);



LumieraPriQueue
lumiera_priqueue_destroy (LumieraPriQueue self);


/**
 * calls resize to match for at least 'elements' in the queue
 * and then sets low_water to 0, disabling shrinking
 * @note on overflow resize will re-enable low_water if it is not aware of this
 */
LumieraPriQueue
lumiera_priqueue_reserve (LumieraPriQueue self, unsigned elements);


/**
 * supplied/default resize function based on realloc
 * initially allocates an array for 64 elements,
 * doubles this when the high water mark is hit,
 * shrinks at high_water/8-8 (that is, 64 is the minimum size)
 */
LumieraPriQueue
lumiera_priqueue_clib_resize (LumieraPriQueue self);




/**
 * insert a new element into the priority queue
 * the element will be copied
 * @return \c NULL on error
 */
LumieraPriQueue
lumiera_priqueue_insert (LumieraPriQueue self, void* element);


/**
 * @return pointer to the topmost element, \NULL on empty queue
 * @note returned pointer is only valid as long
 *       as no insert or remove is called
 */
static inline void*
lumiera_priqueue_peek (LumieraPriQueue self)
{
  if (self && self->queue)
    return self->queue;

  return NULL;
}


/**
 * removes the topmost element
 * @return \c NULL on error (empty queue, resize failure)
 */
LumieraPriQueue
lumiera_priqueue_remove (LumieraPriQueue self);



#endif/*LIB_PRIQUEUE_H*/
