/**
 * Copyright (C) 2008 Elena-Ramona Modroiu (asipto.com)
 *
 * This file is part of kamailio, a free SIP server.
 *
 * 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
 */

/*! \file
 * \ingroup sqlops
 * \brief Kamailio SQL-operations :: Variables
 *
 * - Module: \ref sqlops
 */


#include "../../core/mem/mem.h"
#include "../../core/dprint.h"
#include "../../core/mod_fix.h"

#include "sql_api.h"
#include "sql_var.h"

typedef struct _sql_pv
{
	str resname;
	sql_result_t *res;
	int type;
	gparam_t row;
	gparam_t col;
} sql_pv_t;

int pv_get_dbr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
{
	sql_pv_t *spv;
	int row;
	int col;

	spv = (sql_pv_t *)param->pvn.u.dname;

	if(spv->res == NULL) {
		spv->res = sql_get_result(&spv->resname);
		if(spv->res == NULL)
			return pv_get_null(msg, param, res);
	}

	switch(spv->type) {
		case 1:
			return pv_get_sintval(msg, param, res, spv->res->nrows);
			break;
		case 2:
			return pv_get_sintval(msg, param, res, spv->res->ncols);
			break;
		case 3:
			if(fixup_get_ivalue(msg, &spv->row, &row) != 0)
				return pv_get_null(msg, param, res);
			if(fixup_get_ivalue(msg, &spv->col, &col) != 0)
				return pv_get_null(msg, param, res);
			if(row >= spv->res->nrows)
				return pv_get_null(msg, param, res);
			if(col >= spv->res->ncols)
				return pv_get_null(msg, param, res);
			if(spv->res->vals[row][col].flags & PV_VAL_NULL)
				return pv_get_null(msg, param, res);
			if(spv->res->vals[row][col].flags & PV_VAL_INT)
				return pv_get_sintval(
						msg, param, res, spv->res->vals[row][col].value.n);
			return pv_get_strval(
					msg, param, res, &spv->res->vals[row][col].value.s);
			break;
		case 4:
			if(fixup_get_ivalue(msg, &spv->col, &col) != 0)
				return pv_get_null(msg, param, res);
			if(col >= spv->res->ncols)
				return pv_get_null(msg, param, res);
			return pv_get_strval(msg, param, res, &spv->res->cols[col].name);
			break;
	}
	return 0;
}


int sql_parse_index(str *in, gparam_t *gp)
{
	if(in->s[0] == PV_MARKER) {
		gp->type = GPARAM_TYPE_PVS;
		gp->v.pvs = (pv_spec_t *)pkg_malloc(sizeof(pv_spec_t));
		if(gp->v.pvs == NULL) {
			LM_ERR("no pkg memory left for pv_spec_t\n");
			return -1;
		}

		if(pv_parse_spec(in, gp->v.pvs) == NULL) {
			LM_ERR("invalid PV identifier\n");
			pkg_free(gp->v.pvs);
			return -1;
		}
	} else {
		gp->type = GPARAM_TYPE_INT;
		if(str2sint(in, &gp->v.i) != 0) {
			LM_ERR("bad number <%.*s>\n", in->len, in->s);
			return -1;
		}
	}
	return 0;
}

int pv_parse_dbr_name(pv_spec_p sp, str *in)
{
	sql_pv_t *spv = NULL;
	char *p;
	str pvs;
	str tok;

	spv = (sql_pv_t *)pkg_malloc(sizeof(sql_pv_t));
	if(spv == NULL)
		return -1;

	memset(spv, 0, sizeof(sql_pv_t));

	p = in->s;

	while(p < in->s + in->len
			&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
		p++;
	if(p > in->s + in->len || *p == '\0')
		goto error;
	spv->resname.s = p;
	while(p < in->s + in->len) {
		if(*p == '=' || *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
			break;
		p++;
	}
	if(p > in->s + in->len || *p == '\0')
		goto error;
	spv->resname.len = p - spv->resname.s;
	spv->res = sql_get_result(&spv->resname);

	if(*p != '=') {
		while(p < in->s + in->len
				&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
			p++;
		if(p > in->s + in->len || *p == '\0' || *p != '=')
			goto error;
	}
	p++;
	if(*p != '>')
		goto error;
	p++;

	pvs.len = in->len - (int)(p - in->s);
	pvs.s = p;
	p = pvs.s + pvs.len - 1;
	while(p > pvs.s && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
		p--;
	if(p == pvs.s) {
		LM_ERR("invalid key in [%.*s]\n", in->len, in->s);
		goto error;
	}
	pvs.len = p - pvs.s + 1;

	LM_DBG("res [%.*s] - key [%.*s]\n", spv->resname.len, spv->resname.s,
			pvs.len, pvs.s);
	if(pvs.len == 4 && strncmp(pvs.s, "rows", 4) == 0) {
		spv->type = 1;
	} else if(pvs.len == 4 && strncmp(pvs.s, "cols", 4) == 0) {
		spv->type = 2;
	} else if(pvs.s[0] == '[') {
		spv->type = 3;
		p = pvs.s + 1;
		while(p < in->s + in->len
				&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
			p++;
		if(p > in->s + in->len || *p == '\0')
			goto error_index;
		tok.s = p;
		while(p < in->s + in->len) {
			if(*p == ',' || *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
				break;
			p++;
		}
		if(p > in->s + in->len || *p == '\0')
			goto error_index;
		tok.len = p - tok.s;
		if(sql_parse_index(&tok, &spv->row) != 0)
			goto error_index;
		while(p < in->s + in->len
				&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
			p++;
		if(p > in->s + in->len || *p == '\0' || *p != ',')
			goto error_index;
		p++;
		while(p < in->s + in->len
				&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
			p++;
		if(p > in->s + in->len || *p == '\0')
			goto error_index;
		tok.s = p;
		while(p < in->s + in->len) {
			if(*p == ']' || *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
				break;
			p++;
		}
		if(p > in->s + in->len || *p == '\0')
			goto error_index;
		tok.len = p - tok.s;
		if(sql_parse_index(&tok, &spv->col) != 0)
			goto error_index;
		while(p < in->s + in->len
				&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
			p++;
		if(p > in->s + in->len || *p == '\0' || *p != ']')
			goto error_index;
	} else if(pvs.len > 9 && strncmp(pvs.s, "colname", 7) == 0) {
		spv->type = 4;
		p = pvs.s + 7;
		while(p < in->s + in->len
				&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
			p++;
		if(p > in->s + in->len || *p == '\0' || *p != '[')
			goto error_index;
		p++;
		tok.s = p;
		while(p < in->s + in->len) {
			if(*p == ']' || *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
				break;
			p++;
		}
		if(p > in->s + in->len || *p == '\0')
			goto error_index;
		tok.len = p - tok.s;
		if(sql_parse_index(&tok, &spv->col) != 0)
			goto error_index;
		while(p < in->s + in->len
				&& (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
			p++;
		if(p > in->s + in->len || *p == '\0' || *p != ']')
			goto error_index;
	} else {
		LM_ERR("unknown key [%.*s]\n", pvs.len, pvs.s);
		if(spv != NULL)
			pkg_free(spv);
		return -1;
	}
	sp->pvp.pvn.u.dname = (void *)spv;
	sp->pvp.pvn.type = PV_NAME_PVAR;

	return 0;

error:
	LM_ERR("invalid pv name [%.*s]\n", in->len, in->s);
	if(spv != NULL)
		pkg_free(spv);
	return -1;

error_index:
	LM_ERR("invalid index in [%.*s]\n", pvs.len, pvs.s);
	if(spv != NULL)
		pkg_free(spv);
	return -1;
}
