/*
    zptrarray.c - extended glibc's GPtrArray
    Copyright (C) 2002-2013  Ladislav Vaiz <ok1zia@nagano.cz>
    and authors of glib www.gtk.org

*/


/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
 */

/* 
 * MT safe
 */

#include <zthread.h>
#include <zptrarray.h>

#include <string.h>
#include <stdlib.h>
//#include <glib.h>*/

#define MIN_ARRAY_SIZE  16



static gint g_nearest_pow (gint num);

static gint g_nearest_pow (gint num)
{
    gint n = 1;

    while (n < num)
        n <<= 1;

    return n;
}

/* Index arr
 */

typedef struct _GRealIndexArray GRealIndexArray;

struct _GRealIndexArray {
    gpointer *pdata;
    guint len;
    guint alloc;
};

static void z_ptr_array_maybe_expand (GRealIndexArray * arr, gint len);

#ifdef Z_LEAK_DEBUG_LIST
char *index_array_mem_chunk_file;
int index_array_mem_chunk_line;
#endif


ZPtrArray *z_ptr_array_new (void)
{
    GRealIndexArray *arr;

    //G_LOCK (index_array_mem_chunk);
    /*MUTEX_LOCK (index_array_mem_chunk);
    if (!index_array_mem_chunk)
        index_array_mem_chunk = g_mem_chunk_new ("arr mem chunk",
                                                 sizeof (GRealIndexArray),
                                                 1024, G_ALLOC_AND_FREE);

    arr = g_chunk_new (GRealIndexArray, index_array_mem_chunk);
    //G_UNLOCK (index_array_mem_chunk);
    MUTEX_UNLOCK (index_array_mem_chunk);*/
	arr = g_new(GRealIndexArray, 1);

    arr->pdata = NULL;
    arr->len = 0;
    arr->alloc = 0;

    return (ZPtrArray *) arr;
}

void z_ptr_array_free (ZPtrArray * arr, gboolean free_segment)
{
    g_return_if_fail (arr);

    if (free_segment && arr->pdata != NULL)
        g_free (arr->pdata);

/*    //G_LOCK (index_array_mem_chunk);
    MUTEX_LOCK (index_array_mem_chunk);
    g_mem_chunk_free (index_array_mem_chunk, arr);
    //G_UNLOCK (index_array_mem_chunk);
    MUTEX_UNLOCK (index_array_mem_chunk);*/
	g_free(arr);
    
}

static void z_ptr_array_maybe_expand (GRealIndexArray * arr, gint len)
{
    guint old_alloc;

    if ((arr->len + len) > arr->alloc) {
        old_alloc = arr->alloc;

        arr->alloc = g_nearest_pow (arr->len + len);
        arr->alloc = MAX (arr->alloc, MIN_ARRAY_SIZE);
        if (arr->pdata)
            arr->pdata = (gpointer *)g_realloc (arr->pdata, sizeof (gpointer) * arr->alloc);
        else
            arr->pdata = g_new0 (gpointer, arr->alloc);

        memset (arr->pdata + old_alloc, 0,
                sizeof (gpointer) * (arr->alloc - old_alloc));
    }
}

void z_ptr_array_set_size (ZPtrArray * farray, gint length)
{
    GRealIndexArray *arr = (GRealIndexArray *) farray;

    g_return_if_fail (arr);

    if ((guint)length > arr->len)
        z_ptr_array_maybe_expand (arr, (length - arr->len));

    arr->len = length;
}

gpointer z_ptr_array_remove_index (ZPtrArray * farray, guint index)
{
    GRealIndexArray *arr = (GRealIndexArray *) farray;
    gpointer result;

    g_return_val_if_fail (arr, NULL);

    g_return_val_if_fail (index < arr->len, NULL);

    result = arr->pdata[index];

    if (index != arr->len - 1){
        memmove (arr->pdata + index, arr->pdata + index + 1,
                   sizeof (gpointer) * (arr->len - index - 1));
    }

    arr->pdata[arr->len - 1] = NULL;

    arr->len -= 1;

    return result;
}

gpointer z_ptr_array_remove_index_fast (ZPtrArray * farray, guint index)
{
    GRealIndexArray *arr = (GRealIndexArray *) farray;
    gpointer result;

    g_return_val_if_fail (arr, NULL);

    g_return_val_if_fail (index < arr->len, NULL);

    result = arr->pdata[index];

    if (index != arr->len - 1)
        arr->pdata[index] = arr->pdata[arr->len - 1];

    arr->pdata[arr->len - 1] = NULL;

    arr->len -= 1;

    return result;
}

gboolean z_ptr_array_remove (ZPtrArray * farray, gpointer data)
{
    GRealIndexArray *arr = (GRealIndexArray *) farray;
    guint i;

    g_return_val_if_fail (arr, FALSE);

    for (i = 0; i < arr->len; i += 1) {
        if (arr->pdata[i] == data) {
            z_ptr_array_remove_index (farray, i);
            return TRUE;
        }
    }

    return FALSE;
}

gboolean z_ptr_array_remove_fast (ZPtrArray * farray, gpointer data)
{
    GRealIndexArray *arr = (GRealIndexArray *) farray;
    guint i;

    g_return_val_if_fail (arr, FALSE);

    for (i = 0; i < arr->len; i += 1) {
        if (arr->pdata[i] == data) {
            z_ptr_array_remove_index_fast (farray, i);
            return TRUE;
        }
    }

    return FALSE;
}

void z_ptr_array_add (ZPtrArray * farray, gpointer data)
{
    GRealIndexArray *arr = (GRealIndexArray *) farray;

    g_return_if_fail (arr);

    z_ptr_array_maybe_expand (arr, 1);

    arr->pdata[arr->len++] = data;
}


/* new stuff */

void z_ptr_array_qsort (ZPtrArray *farray, 
        int (*compar)(const void *, const void *)){
    
    GRealIndexArray *arr = (GRealIndexArray *) farray;

    qsort( (void *)arr->pdata, arr->len, sizeof(gpointer), compar);
}

void z_ptr_array_uniq (ZPtrArray *farray, 
        int (*compar)(const void *, const void *), int free_dups){
    
    guint i=0;

    while (i + 1 < farray->len){
        if (compar(farray->pdata+i, farray->pdata+i+1) == 0){
            if (free_dups) g_free(z_ptr_array_index(farray, i + 1));
            z_ptr_array_remove_index(farray, i + 1);
        }else{
            i++;
        }

    }
}

gpointer *z_ptr_array_bsearch(ZPtrArray *farray, const void *key,
        int (*compar)(const void *, const void *)){
    
    GRealIndexArray *arr = (GRealIndexArray *) farray;
    
    return (gpointer) bsearch (key, 
                (void *)arr->pdata, 
                arr->len, 
                sizeof(gpointer), 
                compar);
}

gint z_ptr_array_bsearch_index(ZPtrArray *farray, const void *key,
        int (*compar)(const void *, const void *)){
    gpointer found;
    gint index;

    found = z_ptr_array_bsearch(farray, key, compar);
    if (!found) return -1;
    
    index = (gpointer *)found - (gpointer*)farray->pdata;
    
    return index;
}

void z_ptr_array_free_all(ZPtrArray  *arr){
    int i;
    gpointer item;
            
    for (i = arr->len-1; i >= 0; i--){                   
        item = z_ptr_array_index(arr, i);
        g_free(item);
    }
    arr->len = 0;   
    z_ptr_array_free(arr, arr->pdata ? TRUE : FALSE);
}

void z_ptr_array_insert(ZPtrArray * farray, gpointer data, guint index)
{
    GRealIndexArray *arr = (GRealIndexArray *) farray;

    g_return_if_fail (arr);

    z_ptr_array_maybe_expand (arr, 1);
	if (index > arr->len) index = arr->len;
	if (arr->len - index > 0)	memmove(arr->pdata + index + 1, arr->pdata + index, (arr->len - index) * sizeof(gpointer));
    arr->pdata[index] = data;
	arr->len++;
}

