/* 'checkit_tiff' is a conformance checker for baseline TIFFs
 *
 * author: Andreas Romeyke, 2015-2022
 * licensed under conditions of libtiff
 * (see http://libtiff.maptools.org/misc.html)
 *
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h> /* reallocarray requires _GNU_SOURCE */
#include <assert.h>
#include "ctstring.h"

string_t str(const char * cstr) {
    assert(cstr != NULL);
    string_t s;
    s.is_const=false;
    s.len = strlen( cstr);
#ifdef DEBUG_OVERFLOWS
    assert(s.len < 8192); /* to find "overflows" if pointer points somewhere */
#endif
/* strndup only needed if we need to copy it */
#ifdef HAVE_STRNDUP
    s.string = strndup(cstr, s.len);
#else
    /* hint: change your operating system! */
    s.string = strdup(cstr);
#endif
    return s;
}


string_t const_str(const char * cstr) {
    assert(cstr != NULL);
    string_t s;
    s.is_const=true;
    s.len = strlen( cstr);
    assert(s.len < 8192); /* to find "overflows" if pointer points somewhere */
    s.string = (char *) cstr;
    return s;
}

static const string_t empty = {
        .string = "",
        .len=0,
        .is_const=true
};

string_t empty_str(void) {
    return empty;
}

const char * cstr_of_str (const string_t s) {
    if (s.len > 0) {
        return s.string;
    }
    return "";
}

size_t strlen_of_str (const string_t s) {
    return s.len;
}


/* dest should not exceed maxsize */
stringbuf_t secstrcat_string (stringbuf_t dest, const string_t src) {
    assert(src.len >= 0);
    assert(src.string != NULL);
#ifdef DEBUG_OVERFLOWS
    assert(src.len < 8192); /* to find "overflows" if pointer points somewhere */
#endif
    if (dest.pos >= dest.bufsize-1) {
        fprintf(stderr, "max stringbuf size exceeded %zu (%zu)\n", dest.bufsize, dest.pos);
        size_t bufsize=dest.bufsize*2;
#ifdef COMPAT_NEED_REALLOCARRAY
        string_t * newbuf = realloc(dest.strings, bufsize * sizeof(string_t));
#else
        string_t * newbuf = reallocarray(dest.strings, bufsize, sizeof(string_t));
#endif
        if (NULL == newbuf) {
            fprintf( stderr, "could not alloc newbuf to hold %zu string_t elements\n", bufsize);
            exit(EXIT_FAILURE);
        }
        dest.bufsize = bufsize;
        dest.strings = newbuf;
        for (size_t i=dest.pos+1; i< dest.bufsize; i++ ) {
            dest.strings[i]= empty_str();
        }
    }
    dest.strings[dest.pos++] = src;
    return dest;
}

stringbuf_t secstrcat_cstr (const stringbuf_t dest, const char * src) {
    stringbuf_t ret =  secstrcat_string(dest, const_str(src) );
    return ret;
}

//#define secstrcat (stringbuf, arg) _Generic( (arg), const char *: secstrcat_cstr, stringbuf_t: secstrcat_string)(stringbuf, arg)

stringbuf_t  empty_stringbuf(void) {
    stringbuf_t buf;
    buf.pos=0;
    buf.bufsize=INITIAL_STRINGBUFF_SIZE;
    buf.strings = calloc(buf.bufsize, sizeof(string_t));
    if (NULL == buf.strings) {
        fprintf(stderr, "could not allocate memory (src=%s)", __FILE__);
        exit(EXIT_FAILURE);
    }
    for (size_t i=0; i<buf.bufsize; i++) {
        buf.strings[i]=empty_str();
    }
    return buf;
}

void clean_str(string_t * s) {
    if (s->is_const == false) {
        char * string = s->string;
        if (NULL != string) {
            free(string);
            string = NULL;
        }
    }
    s->len=0;
}

void clean_stringbuf( stringbuf_t * buf){
    buf->pos=0;
    buf->bufsize = 0;
    free(buf->strings);
}

void clean_stringbuf_with_all_strings(stringbuf_t * buf) {
    for (size_t i= 0; i< buf->bufsize; i++) {
        clean_str( & buf->strings[i]);
    }
    clean_stringbuf( buf);
}
