/*
    Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:
    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _HAWK_ARR_H_
#define _HAWK_ARR_H_

#include <hawk-cmn.h>

/** \file
 * This file provides a linear dynamic array. It grows dynamically as items
 * are added.
 */

enum hawk_arr_walk_t
{
	HAWK_ARR_WALK_STOP     = 0,
	HAWK_ARR_WALK_FORWARD  = 1,
	HAWK_ARR_WALK_BACKWARD = 2
};

typedef struct hawk_arr_t      hawk_arr_t;
typedef struct hawk_arr_slot_t hawk_arr_slot_t;
typedef enum   hawk_arr_walk_t hawk_arr_walk_t;

#define HAWK_ARR_COPIER_SIMPLE  ((hawk_arr_copier_t)1)
#define HAWK_ARR_COPIER_INLINE  ((hawk_arr_copier_t)2)

#define HAWK_ARR_COPIER_DEFAULT (HAWK_ARR_COPIER_SIMPLE)
#define HAWK_ARR_FREEER_DEFAULT (HAWK_NULL)
#define HAWK_ARR_COMPER_DEFAULT (hawk_arr_dflcomp)
#define HAWK_ARR_KEEPER_DEFAULT (HAWK_NULL)
#define HAWK_ARR_SIZER_DEFAULT  (HAWK_NULL)

#define HAWK_ARR_NIL ((hawk_oow_t)-1)

#define HAWK_ARR_SIZE(arr)        ((arr)->size)
#define HAWK_ARR_CAPA(arr)        ((arr)->capa)
#define HAWK_ARR_TALLY(arr)       ((arr)->tally)

#define HAWK_ARR_SLOT(arr,index)  ((arr)->slot[index])
#define HAWK_ARR_DPTL(arr,index)  ((const hawk_ptl_t*)&(arr)->slot[index]->val)
#define HAWK_ARR_DPTR(arr,index)  ((arr)->slot[index]->val.ptr)
#define HAWK_ARR_DLEN(arr,index)  ((arr)->slot[index]->val.len)

/**
 *  The hawk_arr_copier_t type defines a callback function for slot construction.
 *  A slot is contructed when a user adds data to an array. The user can
 *  define how the data to add can be maintained in the array. A dynamic
 *  array not specified with any copiers stores the data pointer and
 *  the data length into a slot. A special copier HAWK_ARR_COPIER_INLINE copies
 *  the contents of the data a user provided into the slot.
 *
 *  A copier should return the pointer to the copied data. If it fails to copy
 *  data, it may return HAWK_NULL. You need to set a proper freeer to free up
 *  memory allocated for copy.
 */
typedef void* (*hawk_arr_copier_t) (
	hawk_arr_t* arr    /**< array */,
	void*       dptr   /**< pointer to data to copy */,
	hawk_oow_t  dlen   /**< length of data to copy */
);

/**
 * The hawk_arr_freeer_t type defines a slot destruction callback.
 */
typedef void (*hawk_arr_freeer_t) (
	hawk_arr_t* arr    /**< array */,
	void*       dptr   /**< pointer to data to free */,
	hawk_oow_t  dlen   /**< length of data to free */
);

/**
 * The hawk_arr_comper_t type defines a key comparator that is called when
 * the arry needs to compare data. A linear dynamic array is created with a
 * default comparator that performs bitwise comparison.
 *
 * The default comparator compares data in a memcmp-like fashion.
 * It is not suitable when you want to implement a heap of numbers
 * greater than a byte size. You must implement a comparator that
 * takes the whole element and performs comparison in such a case.
 *
 * The comparator should return 0 if the data are the same, a negative
 * integer if the first data is less than the second data, a positive
 * integer otherwise.
 *
 */
typedef int (*hawk_arr_comper_t) (
	hawk_arr_t*  arr    /* array */,
	const void*  dptr1  /* data pointer */,
	hawk_oow_t   dlen1  /* data length */,
	const void*  dptr2  /* data pointer */,
	hawk_oow_t   dlen2  /* data length */
);

/**
 * The hawk_arr_keeper_t type defines a value keeper that is called when
 * a value is retained in the context that it should be destroyed because
 * it is identical to a new value. Two values are identical if their beginning
 * pointers and their lengths are equal.
 */
typedef void (*hawk_arr_keeper_t) (
	hawk_arr_t* arr     /**< array */,
	void*       vptr    /**< pointer to a value */,
	hawk_oow_t  vlen    /**< length of a value */
);

/**
 * The hawk_arr_sizer_t type defines an array size claculator that is called
 * when the array needs to be resized.
 */
typedef hawk_oow_t (*hawk_arr_sizer_t) (
	hawk_arr_t* arr,  /**< array */
	hawk_oow_t  hint  /**< sizing hint */
);

typedef struct hawk_arr_style_t hawk_arr_style_t;

struct hawk_arr_style_t
{
	hawk_arr_copier_t copier; /* data copier */
	hawk_arr_freeer_t freeer; /* data freeer */
	hawk_arr_comper_t comper; /* data comparator */
	hawk_arr_keeper_t keeper; /* data keeper */
	hawk_arr_sizer_t  sizer;  /* size calculator */
};

/**
 * The hawk_arr_style_kind_t type defines the type of predefined
 * callback set for pair manipulation.
 */
enum hawk_arr_style_kind_t
{
	/** store element pointers */
	HAWK_ARR_STYLE_DEFAULT,
	/** copy elements */
	HAWK_ARR_STYLE_INLINE_COPIER
};

typedef enum hawk_arr_style_kind_t  hawk_arr_style_kind_t;

struct hawk_arr_itr_t
{
	hawk_oow_t idx;
};

typedef struct hawk_arr_itr_t hawk_arr_itr_t;

typedef hawk_arr_walk_t (*hawk_arr_walker_t) (
	hawk_arr_t*      arr   /* array */,
	hawk_oow_t       index /* index to the visited slot */,
	void*            ctx   /* user-defined context */
);

/**
 * The hawk_arr_t type defines a linear dynamic array.
 */
struct hawk_arr_t
{
	hawk_gem_t*       gem;
	const hawk_arr_style_t* style;
	hawk_uint8_t      scale;  /* scale factor */
	hawk_uint16_t     inline_slot_xtnsize;
	hawk_oow_t        heap_pos_offset; /* offset in the data element where position
	                                   * is stored when heap operation is performed. */
	hawk_oow_t        size;   /* number of items */
	hawk_oow_t        tally;  /* number of items set */
	hawk_oow_t        capa;   /* capacity */
	hawk_arr_slot_t** slot;
};

/**
 * The hawk_arr_slot_t type defines a linear dynamic array slot
 */
struct hawk_arr_slot_t
{
	hawk_ptl_t val;
};

#if defined(__cplusplus)
extern "C" {
#endif

/**
 * The hawk_get_arr_style() functions returns a predefined callback set for
 * pair manipulation.
 */
HAWK_EXPORT const hawk_arr_style_t* hawk_get_arr_style (
	hawk_arr_style_kind_t kind
);

/**
 * The hawk_arr_open() function creates a linear dynamic array.
 */
HAWK_EXPORT hawk_arr_t* hawk_arr_open (
	hawk_gem_t* gem,
	hawk_oow_t  ext,  /**< extension size in bytes */
	hawk_oow_t  capa  /**< initial array capacity */
);

/**
 * The hawk_arr_close() function destroys a linear dynamic array.
 */
HAWK_EXPORT void hawk_arr_close (
	hawk_arr_t* arr /**< array */
);

/**
 * The hawk_arr_init() function initializes a linear dynamic array.
 */
HAWK_EXPORT int hawk_arr_init (
	hawk_arr_t*  arr,
	hawk_gem_t*  gem,
	hawk_oow_t   capa
);

/**
 * The hawk_arr_fini() function finalizes a linear dynamic array.
 */
HAWK_EXPORT void hawk_arr_fini (
	hawk_arr_t* arr /**< array */
);

#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE void* hawk_arr_getxtn (hawk_arr_t* arr) { return (void*)(arr + 1); }
#else
#define hawk_arr_getxtn(arr) ((void*)((hawk_arr_t*)(arr) + 1))
#endif

/**
 * The hawk_arr_getscale() function returns the scale factor
 */
HAWK_EXPORT int hawk_arr_getscale (
	hawk_arr_t* arr   /**< array */
);

/**
 * The hawk_arr_setscale() function sets the scale factor of the length
 * of a key and a value. A scale factor determines the actual length of
 * a key and a value in bytes. A arr is created with a scale factor of 1.
 * The scale factor should be larger than 0 and less than 256.
 * It is a bad idea to change the scale factor when \a arr is not empty.
 */
HAWK_EXPORT void hawk_arr_setscale (
	hawk_arr_t* arr   /**< array */,
	int scale         /**< scale factor - 1 to 255 */
);

HAWK_EXPORT hawk_arr_copier_t hawk_arr_getcopier (
	hawk_arr_t* arr   /* array */
);

HAWK_EXPORT void hawk_arr_setstyle (
	hawk_arr_t*             arr,
	const hawk_arr_style_t* style
);

HAWK_EXPORT const hawk_arr_style_t* hawk_arr_getstyle (
	hawk_arr_t* arr
);

HAWK_EXPORT hawk_oow_t hawk_arr_getsize (
	hawk_arr_t* arr
);

HAWK_EXPORT hawk_oow_t hawk_arr_getcapa (
	hawk_arr_t* arr
);

HAWK_EXPORT hawk_arr_t* hawk_arr_setcapa (
	hawk_arr_t* arr,
	hawk_oow_t capa
);

HAWK_EXPORT hawk_oow_t hawk_arr_search (
	hawk_arr_t*  arr,
	hawk_oow_t  pos,
	const void* dptr,
	hawk_oow_t  dlen
);

HAWK_EXPORT hawk_oow_t hawk_arr_rsearch (
	hawk_arr_t*  arr,
	hawk_oow_t  pos,
	const void* dptr,
	hawk_oow_t  dlen
);

HAWK_EXPORT hawk_oow_t hawk_arr_upsert (
	hawk_arr_t* arr,
	hawk_oow_t index,
	void*      dptr,
	hawk_oow_t dlen
);

HAWK_EXPORT hawk_oow_t hawk_arr_insert (
	hawk_arr_t* arr,
	hawk_oow_t index,
	void*      dptr,
	hawk_oow_t dlen
);

HAWK_EXPORT hawk_oow_t hawk_arr_update (
	hawk_arr_t* arr,
	hawk_oow_t pos,
	void*      dptr,
	hawk_oow_t dlen
);

/**
 * The hawk_arr_delete() function deletes the as many data as the count
 * from the index. It returns the number of data deleted.
 */
HAWK_EXPORT hawk_oow_t hawk_arr_delete (
	hawk_arr_t* arr,
	hawk_oow_t  index,
	hawk_oow_t  count
);

/**
 *  The hawk_arr_uplete() function deletes data slot without compaction.
 *  It returns the number of data affected.
 */
HAWK_EXPORT hawk_oow_t hawk_arr_uplete (
	hawk_arr_t* arr,
	hawk_oow_t  index,
	hawk_oow_t  count
);

HAWK_EXPORT void hawk_arr_clear (
	hawk_arr_t* arr
);

/**
 * The hawk_arr_getfirstelem() function returns the pointer to the first element holder
 * in an array.
 */
HAWK_EXPORT hawk_ptl_t* hawk_arr_getfirstelem (
	hawk_arr_t*     arr,   /**< array */
	hawk_arr_itr_t* itr    /**< iterator*/
);

/**
 * The hawk_arr_getnextelem() function returns the pointer to the next element holder.
 */
HAWK_EXPORT hawk_ptl_t* hawk_arr_getnextelem (
	hawk_arr_t*      arr,   /**< array */
	hawk_arr_itr_t*  itr    /**< iterator*/
);

/**
 * The hawk_arr_walk() function calls the \a walker function for each
 * element in the array beginning from the first. The \a walker function
 * should return one of #HAWK_ARR_WALK_FORWARD, #HAWK_ARR_WALK_BACKWARD,
 * #HAWK_ARR_WALK_STOP.
 * \return number of calls to the \a walker fucntion made
 */
HAWK_EXPORT hawk_oow_t hawk_arr_walk (
	hawk_arr_t*       arr,
	hawk_arr_walker_t walker,
	void*             ctx
);

/**
 * The hawk_arr_rwalk() function calls the \a walker function for each
 * element in the array beginning from the last. The \a walker function
 * should return one of #HAWK_ARR_WALK_FORWARD, #HAWK_ARR_WALK_BACKWARD,
 * #HAWK_ARR_WALK_STOP.
 * \return number of calls to the \a walker fucntion made
 */
HAWK_EXPORT hawk_oow_t hawk_arr_rwalk (
	hawk_arr_t*       arr,
	hawk_arr_walker_t walker,
	void*            ctx
);

/**
 * The hawk_arr_pushstack() function appends data to the array. It is a utility
 * function to allow stack-like operations over an array. To do so, you should
 * not play with other non-stack related functions.
 */
HAWK_EXPORT hawk_oow_t hawk_arr_pushstack (
	hawk_arr_t* arr,
	void*      dptr,
	hawk_oow_t dlen
);

/**
 * The hawk_arr_popstack() function deletes the last array data. It is a utility
 * function to allow stack-like operations over an array. To do so, you should
 * not play with other non-stack related functions.
 * \note You must not call this function if \a arr is empty.
 */
HAWK_EXPORT void hawk_arr_popstack (
	hawk_arr_t* arr
);

/**
 * The hawk_arr_pushheap() function inserts data to an array while keeping the
 * largest data at position 0. It is a utiltiy funtion to implement a binary
 * max-heap over an array. Inverse the comparator to implement a min-heap.
 * \return number of array elements
 * \note You must not mess up the array with other non-heap related functions
 *       to keep the heap property.
 */
HAWK_EXPORT hawk_oow_t hawk_arr_pushheap (
	hawk_arr_t* arr,
	void*      dptr,
	hawk_oow_t dlen
);

/**
 * The hawk_arr_popheap() function deletes data at position 0 while keeping
 * the largest data at positon 0. It is a utiltiy funtion to implement a binary
 * max-heap over an array.
 * \note You must not mess up the array with other non-heap related functions
 *       to keep the heap property.
 */
HAWK_EXPORT void hawk_arr_popheap (
	hawk_arr_t* arr
);

HAWK_EXPORT void hawk_arr_deleteheap (
	hawk_arr_t* arr,
	hawk_oow_t index
);

HAWK_EXPORT hawk_oow_t hawk_arr_updateheap (
	hawk_arr_t* arr,
	hawk_oow_t index,
	void*      dptr,
	hawk_oow_t dlen
);

HAWK_EXPORT hawk_oow_t hawk_arr_getheapposoffset (
	hawk_arr_t* arr
);

/**
 * The hawk_arr_setheapposoffset() function sets the offset to a position holding
 * field within a data element. It assumes that the field is of the hawk_oow_t type.
 *
 * \code
 * struct data_t
 * {
 *    int v;
 *    hawk_oow_t pos;
 * };
 * struct data_t d;
 * hawk_arr_setheapposoffset (arr, HAWK_OFFSETOF(struct data_t, pos));
 * d.v = 20;
 * hawk_arr_pushheap (arr, &d, 1);
 * \endcode
 *
 * In the code above, the 'pos' field of the first element in the array must be 0.
 *
 * If it's set to HAWK_ARR_NIL, position is not updated when heapification is
 * performed.
 */
HAWK_EXPORT void hawk_arr_setheapposoffset (
	hawk_arr_t* arr,
	hawk_oow_t offset
);



/**
 * The hawk_arr_dflcomp() function is the default comparator
 */
HAWK_EXPORT int hawk_arr_dflcomp (
	hawk_arr_t* arr,
	const void* dptr1,
	hawk_oow_t  dlen1,
	const void* dptr2,
	hawk_oow_t dlen2
);

#if defined(__cplusplus)
}
#endif

#endif
