#ifndef _DWC_STRINGS_C
#define _DWC_STRINGS_C

/*--------------------------------------------------------------------------
 *  Copyright (c) 2001-2004 Igor Daniloff. 
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at this directory.
 *------------------------------------------------------------------------*/
 
#ifndef lint
static const char rcsid[] = "$Id: dwc_strings.c,v 1.24 2005/09/21 09:20:44 asv Exp $";
#endif

#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "dwc_strings.h"
#include "dwc_macro.h"
#if defined(__UNIX_DW)
#include "dwc_lib.h"
#endif /* UNIX */
#if defined(__WIN32_DW)
#include "dw_winlib.h"
#endif /* WIN32 */

typedef struct dwc_string_entry_st {
    define_queue_stuff( struct dwc_string_entry_st );      
    dwc_string_t        str;
} dwc_string_entry_t;

define_queue_of( struct dwc_string_entry_st ) dwc_string_list_entry_t;

typedef struct dw_ml_string_t {
    dwc_length_t            count;
    dwc_length_t            size;
    dwc_length_t            max_size;
    dwc_direction_t         getnextdir;
    dwc_string_entry_t     *current;
    dwc_string_t            cache;
    dwc_direction_t         cache_dir;
    dwc_string_list_entry_t list_header;
} dwc_string_list_t;

typedef struct dwc_mb_entry_st {
    define_queue_stuff( struct dwc_mb_entry_st );      
    dwc_membuf_t        chunk;
    dwc_length_t        chunk_size;
} dwc_mb_entry_t;

define_queue_of( struct dwc_mb_entry_st ) dwc_mb_list_entry_t;

typedef struct dw_mb_list_t {
    dwc_length_t            count;
    dwc_length_t            size;
    dwc_length_t            max_size;
    dwc_mb_list_entry_t     list_header;
} dwc_mb_list_t;

/* -------------------------------------------------------------------- */
dwc_multistring_t dw_multistring_create( dwc_length_t maxsize )
{
    dwc_multistring_t list;
    
    list = DWC_Malloc( struct dw_ml_string_t*, sizeof(struct dw_ml_string_t) );
    if( list )
    {
        DWC_QUEUE_INIT( &list->list_header );
        list->count = 0;
        list->size = 0;
        list->max_size = maxsize;
        list->current = NULL;
        list->getnextdir = DW_SD_FORWARD;
        list->cache = NULL;
        list->cache_dir = DW_SD_FORWARD;
    }
    return list;
}

/* -------------------------------------------------------------------- */
dwc_void_t dw_multistring_delete( dwc_multistring_t list )
{
    dwc_string_entry_t *tmp_var;
    
    if( !list )
    {
        return;
    }
    while( NULL != (tmp_var = DWC_QUEUE_FIRST( &list->list_header )) )
    {   
        DWC_QUEUE_REMOVE( &list->list_header, tmp_var );
        list->size -= strlen( tmp_var->str );
        DWC_Free( tmp_var->str );        
        DWC_Free( tmp_var );
        --list->count;
    }
    DWC_Free(list->cache);
    DWC_Free(list);
}

/* -------------------------------------------------------------------- */
dwc_bool_t dw_multistring_add( dwc_multistring_t list, const char* str )
{
    dwc_string_entry_t *tmp_var;
    dwc_length_t        slen;

    if( !list )
    {
        printLib( DW_LL_ERROR, "dwlib: strings: null list passed" );
        return FALSE;
    }
    if( !str )
    {
        printLib( DW_LL_DEBUG, "dwlib: strings: null string passed" );
        return TRUE;
    }    
    slen = strlen( str );
    if( slen == 0 )
    {
        printLib( DW_LL_DEBUG, "dwlib: strings: zero-length string passed" );
        return TRUE;
    }
    if( list->max_size && (slen + list->size >= list->max_size) )
    {
        printLib( DW_LL_DEBUG, "dwlib: strings: new string(%d) overcome limit (%d) for whole(%d)", slen, list->max_size, list->size ); 
        return FALSE;
    }
    tmp_var = DWC_Malloc( dwc_string_entry_t*, sizeof(dwc_string_entry_t) );
    if( !tmp_var )
    {
        printLib( DW_LL_DEBUG, "dwlib: %s(%d) - cannot allocate memory", __FILE__, __LINE__ );
        return FALSE;
    }
    tmp_var->str = DWC_Strdup( str );
    if( !tmp_var->str )
    {
        printLib( DW_LL_DEBUG, "dwlib: %s(%d) - cannot allocate memory", __FILE__, __LINE__ );
        DWC_Free( tmp_var );
        return FALSE;
    }
    DWC_QUEUE_INSERT_TAIL( &list->list_header, tmp_var );
    ++list->count;
    list->size += slen;
    if( list->cache )
    {
        /* now cache not valid */
        DWC_Free( list->cache );
    }
    return TRUE;
}

/* -------------------------------------------------------------------- */
dwc_bool_t dw_multistring_add_int ( dwc_multistring_t list, const int val )
{
    char    buffer[CHARS_IN_INT+2];
    int     ret;

    if( !list )
    {
        printLib( DW_LL_ERROR, "dwlib: strings: null list passed" );
        return FALSE;
    }
    ret = snprintf( buffer, sizeof(buffer), "%d", val );
    buffer[CHARS_IN_INT] = NULLCHAR;
    if( (ret < 0) || (ret > CHARS_IN_INT) )
    {
        printLib( DW_LL_ERROR, "dwlib: strings: added integer value too big for format" );
        return FALSE;
    }
    return dw_multistring_add( list, buffer );
}

/* -------------------------------------------------------------------- */
dwc_length_t dw_multistring_size( dwc_multistring_t list )
{
    if( list )
        return list->size;
    else
        return 0;
}

/* -------------------------------------------------------------------- */
dwc_length_t dw_multistring_count( dwc_multistring_t list )
{
    if( list )
        return list->count;
    else
        return 0;
}

/* -------------------------------------------------------------------- */
dwc_string_t dw_multistring_concat( dwc_multistring_t list, dwc_direction_t dir )
{
    dwc_length_t        size;
    dwc_string_entry_t *strentry;

    if( !list || ((dir != DW_SD_FORWARD) && (dir != DW_SD_BACKWARD)) )    
    {
        return NULL;
    }
    if( (dir == list->cache_dir) && list->cache )
    {
        /* cache still valid */
        return DWC_Strdup(list->cache);
    }
    if( list->cache )
    {
        DWC_Free( list->cache );
    }
    list->cache_dir = dir;
    size = list->size + 1;
    list->cache = DWC_Malloc( dwc_string_t, size );    
    if( !list->cache )
    {
        return NULL;
    }
    list->cache[0] = NULLCHAR;
    if( dir == DW_SD_FORWARD )
    {
        DWC_QUEUE_FOREACH( strentry, &list->list_header )
        {   
            if( size <= strlen(strentry->str) ) 
            {
                printLib( DW_LL_DEBUG, "dwlib: strings: STRANGE list(%d.%d) with corrupted size", list->size, list->max_size );
                goto end_add;
            }
            strncat( list->cache, strentry->str, size );
            size -= strlen(strentry->str);            
        }
    }    
    else
    {
        DWC_QUEUE_FOREACH_REVERSE( strentry, &list->list_header )
        {
            if( size <= strlen(strentry->str) ) 
            {
                printLib( DW_LL_DEBUG, "dwlib: strings: STRANGE list(%d.%d) with corrupted size", list->size, list->max_size );
                goto end_add;
            }
            strncat( list->cache, strentry->str, size );
            size -= strlen(strentry->str);
        }    
    }
  end_add:    
    list->cache[list->size] = NULLCHAR;
    return DWC_Strdup(list->cache);
}

/* -------------------------------------------------------------------- */
dwc_bool_t dw_multistring_rewind( dwc_multistring_t list, dwc_direction_t dir )
{
    if( !list || ((dir != DW_SD_FORWARD) && (dir != DW_SD_BACKWARD)) )    
    {
        return FALSE;
    }
    list->getnextdir = dir;
    if( dir == DW_SD_FORWARD )
    {
        list->current = DWC_QUEUE_FIRST( &list->list_header );
    }
    else
    {
        list->current = DWC_QUEUE_LAST( &list->list_header );
    }
    return TRUE;
}

/* -------------------------------------------------------------------- */
dwc_string_t dw_multistring_getnext( dwc_multistring_t list )
{
    char *cs;
    
    if( !list )
    {
        return NULL;
    }
    if( !list->current )
    {
        return NULL;
    }
    cs = list->current->str;
    if( list->getnextdir == DW_SD_FORWARD )
    {
        list->current = DWC_QUEUE_NEXT( list->current );
    }
    else
    {
        list->current = DWC_QUEUE_PREV( list->current );
    }
    return cs;
}

/* -------------------------------------------------------------------- */
dwc_bool_t dw_multistring_append( dwc_multistring_t list, dwc_multistring_t str, dwc_string_t sep )
{
    dwc_string_t cur;
    
    if( !list )
        return FALSE;
    if( !str )
        return TRUE;        
    dw_multistring_rewind( str, DW_SD_FORWARD );
    cur = dw_multistring_getnext( str );
    while( cur )
    {
        if( !dw_multistring_add( list, cur ) )
        {
            return FALSE;
        }
        cur = dw_multistring_getnext( str );
        if( cur && sep && !dw_multistring_add( list, sep ) )
        {
           return FALSE;
        }
    }
    return TRUE;
}

/* -------------------------------------------------------------------- */
dwc_bool_t dw_multistring_contain( dwc_multistring_t list, dwc_string_t str )
{
    dwc_string_entry_t *strentry;

    if( !list || !str )
        return FALSE;

    DWC_QUEUE_FOREACH( strentry, &list->list_header )
    {   
        if( 0 == strcmp( strentry->str, str ) )
            return TRUE;
    }
    return FALSE;
}

/* ******************************************************************** */

/* -------------------------------------------------------------------- */
dwc_mbuf_list_t dw_mbuf_create( dwc_length_t maxsize )
{
    dwc_mbuf_list_t list;
    
    list = DWC_Malloc( struct dw_mb_list_t*, sizeof(struct dw_mb_list_t) );
    if( list )
    {
        DWC_QUEUE_INIT( &list->list_header );
        list->count = 0;
        list->size = 0;
        list->max_size = maxsize;
    }
    else
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::create no memory for list header" );
    }
    return list;
}

/* -------------------------------------------------------------------- */
dwc_length_t dw_mbuf_count( dwc_mbuf_list_t list )
{
    if( !list )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::count null list passed" );
        return 0;
    }
    return list->count;
}

/* -------------------------------------------------------------------- */
dwc_length_t dw_mbuf_size( dwc_mbuf_list_t list )
{
    if( !list )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::size null list passed" );
        return 0;
    }
    return list->size;
}

/* -------------------------------------------------------------------- */
dwc_bool_t dw_mbuf_add_chunk( dwc_mbuf_list_t list, dwc_membuf_t chunk, dwc_length_t chunk_size )
{
    dwc_mb_entry_t     *tmp_var;

    if( !list )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::add null list passed" );
        return FALSE;
    }
    if( !chunk )
    {
        printLib( DW_LL_DEBUG, "dwlib: mbufs::add null chunk passed" );
        return TRUE;
    }    
    if( chunk_size == 0 )
    {
        printLib( DW_LL_DEBUG, "dwlib: mbufs::add zero-length chunk passed" );
        return TRUE;
    }
    if( list->max_size && (chunk_size + list->size >= list->max_size) )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::add new chunk (%u bytes) overcome limit (%u bytes) for list (current size %u bytes)", chunk_size, list->max_size, list->size ); 
        return FALSE;
    }
    tmp_var = DWC_Malloc( dwc_mb_entry_t*, sizeof(dwc_mb_entry_t) );
    if( !tmp_var )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::add %s(%d) - cannot allocate memory", __FILE__, __LINE__ );
        return FALSE;
    }     
    tmp_var->chunk = DWC_Malloc( dwc_membuf_t, chunk_size );
    if( !tmp_var->chunk )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::add %s(%d) - cannot allocate memory", __FILE__, __LINE__ );
        DWC_Free( tmp_var );
        return FALSE;
    }
    memcpy( tmp_var->chunk, chunk, chunk_size );
    tmp_var->chunk_size = chunk_size;
    DWC_QUEUE_INSERT_TAIL( &list->list_header, tmp_var );
    ++list->count;
    list->size += chunk_size;
    return TRUE;
}

/* -------------------------------------------------------------------- */
dwc_membuf_t dw_mbuf_to_linear( dwc_mbuf_list_t list, dwc_direction_t dir, dwc_bool_t is_str )
{
    dwc_length_t        size;
    dwc_mb_entry_t     *mbentry;
    dwc_membuf_t        linear_buf;
    dwc_membuf_t        cur_pos;

    if( !list || ((dir != DW_SD_FORWARD) && (dir != DW_SD_BACKWARD)) )    
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::to_linear bad parameters were passed (%p, %d)", list, dir );
        return NULL;
    }
    size = list->size;
    if( is_str )
        ++size;
    linear_buf = DWC_Malloc( dwc_string_t, size );    
    if( !linear_buf )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::to_linear cannot allocate memory" );
        return NULL;
    }
    memset( linear_buf, 0, size );
    cur_pos = linear_buf;
    if( dir == DW_SD_FORWARD )
    {
        DWC_QUEUE_FOREACH( mbentry, &list->list_header )
        {   
            if( size < mbentry->chunk_size ) 
            {
                printLib( DW_LL_DEBUG, "dwlib: mbufs::to_linear WARNING list(%d.%d) has corrupted size", list->size, list->max_size );
                goto end_add;
            }
            memcpy( cur_pos, mbentry->chunk, mbentry->chunk_size );
            size -= mbentry->chunk_size;
            cur_pos += mbentry->chunk_size;
        }
    }    
    else
    {
        DWC_QUEUE_FOREACH_REVERSE( mbentry, &list->list_header )
        {
            if( size < mbentry->chunk_size ) 
            {
                printLib( DW_LL_DEBUG, "dwlib: mbufs::to_linear WARNING list(%d,%d) has corrupted size", list->size, list->max_size );
                goto end_add;
            }
            memcpy( cur_pos, mbentry->chunk, mbentry->chunk_size );
            size -= mbentry->chunk_size;
            cur_pos += mbentry->chunk_size;
        }    
    }    
  end_add:    
    if( is_str )
    {
        linear_buf[list->size] = NULLCHAR;
    }
    return linear_buf;
}

/* -------------------------------------------------------------------- */
dwc_void_t dw_mbuf_delete( dwc_mbuf_list_t list )
{
    dwc_mb_entry_t *tmp_var;
    
    if( !list )
    {
        printLib( DW_LL_ERROR, "dwlib: mbufs::delete null list passed" );
        return;
    }
    while( NULL != (tmp_var = DWC_QUEUE_FIRST( &list->list_header )) )
    {   
        DWC_QUEUE_REMOVE( &list->list_header, tmp_var );
        list->size -= tmp_var->chunk_size;
        DWC_Free( tmp_var->chunk );        
        DWC_Free( tmp_var );
        --list->count;
    }
    DWC_Free(list);
}

#endif /* _DWC_STRINGS_C */
