/*
 * librdkafka - The Apache Kafka C/C++ library
 *
 * Copyright (c) 2016 Magnus Edenhill
 * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 OWNER OR CONTRIBUTORS 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.
 */


#include "rd.h"
#include "rdstring.h"

/**
 * @brief Render string \p template using \p callback for key lookups.
 *
 * Keys in template follow the %{keyname} syntax.
 *
 * The \p callback must not write more than \p size bytes to \p buf, must
 * should return the number of bytes it wanted to write (which will indicate
 * a truncated write).
 * If the key is not found -1 should be returned (which fails the rendering).
 *
 * @returns number of written bytes to \p dest,
 *          or -1 on failure (errstr is written)
 */
char *rd_string_render (const char *template,
			char *errstr, size_t errstr_size,
			ssize_t (*callback) (const char *key,
					     char *buf, size_t size,
					     void *opaque),
			 void *opaque) {
	const char *s = template;
	const char *tend = template + strlen(template);
	size_t size = 256;
	char *buf;
	size_t of = 0;

	buf = rd_malloc(size);

#define _remain() (size - of - 1)
#define _assure_space(SZ) do {				\
		if (of + (SZ) + 1 >= size) {		\
			size = (size + (SZ) + 1) * 2;	\
			buf = realloc(buf, size);	\
		}					\
	} while (0)
	
#define _do_write(PTR,SZ) do {				\
		_assure_space(SZ);			\
		memcpy(buf+of, (PTR), (SZ));		\
		of += (SZ);				\
	} while (0)



	while (*s) {
		const char *t;
		size_t tof = (size_t)(s-template);

		t = strstr(s, "%{");
		if (t != s) {
			/* Write "abc%{" 
			 *        ^^^ */
			size_t len = (size_t)((t ? t : tend)-s);
			if (len)
				_do_write(s, len);
		}

		if (t) {
			const char *te;
			ssize_t r;
			char *tmpkey;

			/* Find "abc%{key}"
			 *               ^ */
			te = strchr(t+2, '}');
			if (!te) {
				rd_snprintf(errstr, errstr_size,
					    "Missing close-brace } for "
					    "%.*s at %"PRIusz,
					    15, t, tof);
				rd_free(buf);
				return NULL;
			}

			rd_strndupa(&tmpkey, t+2, (int)(te-t-2));

			/* Query callback for length of key's value. */
			r = callback(tmpkey, NULL, 0, opaque);
			if (r == -1) {
				rd_snprintf(errstr, errstr_size,
					    "Property not available: \"%s\"",
					    tmpkey);
				rd_free(buf);
				return NULL;
			}

			_assure_space(r);

			/* Call again now providing a large enough buffer. */
			r = callback(tmpkey, buf+of, _remain(), opaque);
			if (r == -1) {
				rd_snprintf(errstr, errstr_size,
					    "Property not available: "
					    "\"%s\"", tmpkey);
				rd_free(buf);
				return NULL;
			}

			assert(r < (ssize_t)_remain());
			of += r;
			s = te+1;

		} else {
			s = tend;
		}
	}

	buf[of] = '\0';
	return buf;
}




void rd_strtup_destroy (rd_strtup_t *strtup) {
        rd_free(strtup);
}

rd_strtup_t *rd_strtup_new (const char *name, const char *value) {
        size_t name_sz = strlen(name) + 1;
        size_t value_sz = strlen(value) + 1;
        rd_strtup_t *strtup;

        strtup = rd_malloc(sizeof(*strtup) +
                           name_sz + value_sz - 1/*name[1]*/);
        memcpy(strtup->name, name, name_sz);
        strtup->value = &strtup->name[name_sz];
        memcpy(strtup->value, value, value_sz);

        return strtup;
}



/**
 * @brief Convert bit-flags in \p flags to human-readable CSV string
 *        use the bit-description strings in \p desc.
 *
 *        \p desc array element N corresponds to bit (1<<N).
 *        \p desc MUST be terminated by a NULL array element.
 *        Empty descriptions are ignored even if the bit is set.
 *
 * @returns a null-terminated \p dst
 */
char *rd_flags2str (char *dst, size_t size,
                    const char **desc, int flags) {
        int bit = 0;
        size_t of = 0;

        for ( ; *desc ; desc++, bit++) {
                int r;

                if (!(flags & (1 << bit)) || !*desc)
                        continue;

                if (of >= size) {
                        /* Dest buffer too small, indicate truncation */
                        if (size > 3)
                                rd_snprintf(dst+(size-3), 3, "..");
                        break;
                }

                r = rd_snprintf(dst+of, size-of, "%s%s",
                                !of ? "" : ",", *desc);

                of += r;
        }

        if (of == 0 && size > 0)
                *dst = '\0';

        return dst;
}
