/*
 * Flatstore module interface
 *
 * Copyright (C) 2004 FhG Fokus
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Kamailio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * Kamailio 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <string.h>
#include <ctype.h>
#include "../../core/mem/mem.h"
#include "../../core/dprint.h"
#include "km_flat_pool.h"
#include "km_flat_con.h"
#include "km_flatstore_mod.h"
#include "km_flatstore.h"
#include "db_flatstore.h"


static int parse_flat_url(const str *url, str *path)
{
	if(!url || !url->s || !path) {
		LM_ERR("invalid parameter value\n");
		return -1;
	}
	path->s = strchr(url->s, ':') + 1;
	path->len = strlen(path->s);
	return 0;
}


/*
 * Initialize database module
 * No function should be called before this
 */
db1_con_t *flat_db_init(const str *url)
{
	db1_con_t *res;
	str *path;

	if(!url || !url->s) {
		LM_ERR("invalid parameter value\n");
		return 0;
	}

	/* We do not know the name of the table (and the name of the corresponding
	 * file) at this point, we will simply store the path taken from the url
	 * parameter in the table variable, flat_use_table will then pick that
	 * value and open the file
	 */
	/* as the table (path) is a substring of the received str, we need to
	 * allocate a separate str struct for it -bogdan
	 */
	res = pkg_malloc(
			sizeof(db1_con_t) + sizeof(struct flat_con *) + sizeof(str));
	if(!res) {
		PKG_MEM_ERROR;
		return 0;
	}
	memset(res, 0, sizeof(db1_con_t) + sizeof(struct flat_con *) + sizeof(str));
	path = (str *)(((char *)res) + sizeof(db1_con_t)
				   + sizeof(struct flat_con *));

	if(parse_flat_url(url, path) < 0) {
		pkg_free(res);
		return 0;
	}
	res->table = path;

	return res;
}


/*
 * Store name of table that will be used by
 * subsequent database functions
 */
int flat_use_table(db1_con_t *h, const str *t)
{
	struct flat_con *con;

	if(!h || !t || !t->s) {
		LM_ERR("invalid parameter value\n");
		return -1;
	}

	if(CON_TABLE(h)->s != t->s) {
		if(CON_TAIL(h)) {
			/* Decrement the reference count
			 * of the connection but do not remove
			 * it from the connection pool
			 */
			con = (struct flat_con *)CON_TAIL(h);
			con->ref--;
		}

		CON_TAIL(h) = (unsigned long)flat_get_connection(
				(char *)CON_TABLE(h)->s, (char *)t->s);
		if(!CON_TAIL(h)) {
			return -1;
		}
	}

	return 0;
}


void flat_db_close(db1_con_t *h)
{
	struct flat_con *con;

	if(!h) {
		LM_ERR("invalid parameter value\n");
		return;
	}

	con = (struct flat_con *)CON_TAIL(h);

	if(con) {
		flat_release_connection(con);
	}
	pkg_free(h);
}


/*
 * Insert a row into specified table
 * h: structure representing database connection
 * k: key names
 * v: values of the keys
 * n: number of key=value pairs
 */
int flat_db_insert(
		const db1_con_t *h, const db_key_t *k, const db_val_t *v, const int n)
{
	FILE *f;
	int i;
	int l;
	char *s, *p;

	if(km_local_timestamp < *km_flat_rotate) {
		flat_rotate_logs();
		km_local_timestamp = *km_flat_rotate;
	}

	f = CON_FILE(h);
	if(!f) {
		LM_ERR("uninitialized connection\n");
		return -1;
	}

	for(i = 0; i < n; i++) {
		switch(VAL_TYPE(v + i)) {
			case DB1_INT:
				fprintf(f, "%d", VAL_INT(v + i));
				break;

			case DB1_UINT:
				fprintf(f, "%u", VAL_UINT(v + i));
				break;

			case DB1_BIGINT:
				fprintf(f, "%lld", VAL_BIGINT(v + i));
				break;

			case DB1_UBIGINT:
				fprintf(f, "%llu", VAL_UBIGINT(v + i));
				break;

			case DB1_DOUBLE:
				fprintf(f, "%f", VAL_DOUBLE(v + i));
				break;

			case DB1_STRING:
				fprintf(f, "%s", VAL_STRING(v + i));
				break;

			case DB1_STR:
				if(!encode_delimiter) {
					fprintf(f, "%.*s", VAL_STR(v + i).len, VAL_STR(v + i).s);
				} else {
					s = VAL_STR(v + i).s;
					l = VAL_STR(v + i).len;
					while(l--) {
						if(*s == *flat_delimiter.s) {
							fprintf(f, "%%%02X", (*s & 0xff));
						} else {
							fprintf(f, "%c", *s);
						}
						++s;
					}
				}
				break;

			case DB1_DATETIME:
				fprintf(f, "%llu", (unsigned long long)VAL_TIME(v + i));
				break;

			case DB1_BLOB:
				l = VAL_BLOB(v + i).len;
				s = p = VAL_BLOB(v + i).s;
				while(l--) {
					if(!(isprint((int)*s) && *s != '\\' && *s != '|')) {
						fprintf(f, "%.*s\\x%02X", (int)(s - p), p, (*s & 0xff));
						p = s + 1;
					}
					++s;
				}
				if(p != s)
					fprintf(f, "%.*s", (int)(s - p), p);
				break;

			case DB1_BITMAP:
				fprintf(f, "%u", VAL_BITMAP(v + i));
				break;

			default:
				LM_ERR("val type [%d] not supported", VAL_TYPE(v + i));
				return -1;
		}

		if(i < (n - 1)) {
			fprintf(f, "%c", *km_flat_delimiter);
		}
	}

	fprintf(f, "\n");

	if(flat_flush) {
		fflush(f);
	}

	return 0;
}
