/* Copyright (c) 2007-2009 by Adalin B.V.
 * Copyright (c) 2007-2009 by Erik Hofman
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *      * 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.
 *      * Neither the name of (any of) the copyrightholder(s) nor the
 *         names of its contributors may be used to endorse or promote products
 *         derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * 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.
 */


#include <sys/types.h>
#include <stdlib.h>
#include <assert.h>

#include "xml.h"

#ifndef NDEBUG
# define PRINT(a, b, c) { \
   size_t l1 = (b), l2 = (c); \
   char *s = (a); \
   if (s) { \
      size_t q, len = l2; \
      if (l1 < l2) len = l1; \
      if (len < 50000) { \
         printf("(%i) '", len); \
         for (q=0; q<len; q++) printf("%c", s[q]); \
         printf("'\n"); \
      } else printf("Length (%u) seems too large at line %i\n",len, __LINE__); \
   } else printf("NULL pointer at line %i\n", __LINE__); \
}
#endif

#if !defined(XML_USE_NODECACHE)
void *
cacheNodeGet(void *id, const char *node)
{
    return 0;
}

#else

#define NODE_BLOCKSIZE          16

struct _xml_node
{
    void *parent;
    char *name;
    size_t name_len;
    char *data;
    size_t data_len;
    void **node;
    size_t no_nodes;
    size_t first_free;
};

char *
__xmlNodeGetFromCache(void **nc, const char *start, size_t *len,
                      char **element, size_t *elementlen , size_t *nodenum)
{
    struct _xml_node *cache;
    size_t num = *nodenum;
    char *name = *element;
    void *rv = 0;

    assert(nc != 0);
 
    cache = (struct _xml_node *)*nc;
    assert(cache != 0);

    assert((cache->first_free > num) || (cache->first_free == 0));

    if (cache->first_free == 0) /* leaf node */
    {
        rv = cache->data;
        *len = cache->data_len;
        *element = cache->name;
        *elementlen = cache->name_len;
        *nodenum = 0;
    }
    else if (*name == '*')
    {
        struct _xml_node *node = cache->node[num];
        *nc = node;
        rv = node->data;
        *len = node->data_len;
        *element = node->name;
        *elementlen = node->name_len;
        *nodenum = cache->first_free;
    }
    else
    {
        size_t namelen = *elementlen;
        size_t i, pos = 0;

        for (i=0; i<cache->first_free; i++)
        {
            struct _xml_node *node = cache->node[i];

            assert(node);

            if ((node->name_len == namelen) &&
                (!strncasecmp(node->name, name, namelen)))
            {
                 if (pos == num)
                 {
                     *nc = node;
                     rv = node->data;
                     *element = node->name;
                     *elementlen = node->name_len;
                     *len = node->data_len;
                     *nodenum = cache->first_free;
                     break;
                 }
                 pos++;
            }
        }
    }

    return rv;
}


void *
cacheInit()
{
   return calloc(1, sizeof(struct _xml_node));
}

void
cacheInitLevel(void *nc)
{
    struct _xml_node *cache = (struct _xml_node *)nc;

    assert(cache != 0);

    cache->node = calloc(NODE_BLOCKSIZE, sizeof(struct _xml_node *));
    cache->no_nodes = NODE_BLOCKSIZE;
}

void
cacheFree(void *nc)
{
    struct _xml_node *cache = (struct _xml_node *)nc;

    assert(nc != 0);

    if (cache->first_free)
    {
        struct _xml_node **node = (struct _xml_node **)cache->node;
        size_t i = 0;

        while(i < cache->first_free)
        {
            cacheFree(node[i++]);
        }

        free(node);
    }
    free(cache);
}

void *
cacheNodeGet(void *id)
{
    struct _xml_id *xid = (struct _xml_id *)id;
    struct _xml_node *cache = 0;

    assert(xid != 0);

    if (xid->name)
    {
        cache = xid->node;
    }
    else
    {
        struct _root_id *rid = (struct _root_id *)xid;
        cache = rid->node;
    }

    return cache;
}

void *
cacheNodeNew(void *nc)
{
    struct _xml_node *cache = (struct _xml_node *)nc;
    struct _xml_node *rv = 0;
    size_t i = 0;

    assert(nc != 0);

    i = cache->first_free;
    if (i == cache->no_nodes)
    {
        size_t size, no_nodes;
        void *p;

        no_nodes = cache->no_nodes + NODE_BLOCKSIZE;
        size = no_nodes * sizeof(struct _xml_node *);
        p = realloc(cache->node, size);
        if (!p) return 0;

        cache->node = p;
        cache->no_nodes = no_nodes;
    }

    rv = calloc(1, sizeof(struct _xml_node));
    if (rv) rv->parent = cache;
    cache->node[i] = rv;
    cache->first_free++;

    return rv;
}

void
cacheDataSet(void *n, char *name, size_t namelen, char *data, size_t datalen)
{
   struct _xml_node *node = (struct _xml_node *)n;

   assert(node != 0);
   assert(name != 0);
   assert(namelen != 0);
   assert(data != 0);

   node->name = name;
   node->name_len = namelen;
   node->data = data;
   node->data_len = datalen;
}

#endif