/*
 *
 *  AT chat library with GLib integration
 *
 *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <stdio.h>

#include <glib.h>

#include "gatresult.h"

void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result)
{
	iter->result = result;
	iter->pre.next = result->lines;
	iter->pre.data = NULL;
	iter->l = &iter->pre;
	iter->line_pos = 0;
}

gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix)
{
	char *line;
	int prefix_len = prefix ? strlen(prefix) : 0;
	int linelen;

	while ((iter->l = iter->l->next)) {
		line = iter->l->data;
		linelen = strlen(line);

		if (linelen > G_AT_RESULT_LINE_LENGTH_MAX)
			continue;

		if (prefix_len == 0) {
			iter->line_pos = 0;
			goto out;
		}

		if (g_str_has_prefix(line, prefix) == FALSE)
			continue;

		iter->line_pos = prefix_len;

		while (iter->line_pos < strlen(line) &&
			line[iter->line_pos] == ' ')
			iter->line_pos += 1;

		goto out;
	}

	return FALSE;

out:
	/* Already checked the length to be no more than buflen */
	strcpy(iter->buf, line);
	return TRUE;
}

const char *g_at_result_iter_raw_line(GAtResultIter *iter)
{
	const char *line;

	if (iter == NULL)
		return NULL;

	if (iter->l == NULL)
		return NULL;

	line = iter->l->data;

	line += iter->line_pos;

	return line;
}

static inline int skip_to_next_field(const char *line, int pos, int len)
{
	if (pos < len && line[pos] == ',')
		pos += 1;

	while (pos < len && line[pos] == ' ')
		pos += 1;

	return pos;
}

gboolean g_at_result_iter_next_unquoted_string(GAtResultIter *iter,
						const char **str)
{
	unsigned int pos;
	unsigned int end;
	unsigned int len;
	unsigned int stripped;
	char *line;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	pos = iter->line_pos;

	/* Omitted string */
	if (line[pos] == ',') {
		end = pos;
		iter->buf[pos] = '\0';
		goto out;
	}

	if (line[pos] == '"' || line[pos] == ')')
		return FALSE;

	end = pos;

	while (end < len && line[end] != ',' && line[end] != ')')
		end += 1;

	stripped = end;

	while (line[stripped - 1] == ' ')
		stripped -= 1;

	iter->buf[stripped] = '\0';

out:
	iter->line_pos = skip_to_next_field(line, end, len);

	if (str)
		*str = iter->buf + pos;

	return TRUE;
}

gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str)
{
	unsigned int pos;
	unsigned int end;
	unsigned int len;
	char *line;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	pos = iter->line_pos;

	/* Omitted string */
	if (line[pos] == ',') {
		end = pos;
		iter->buf[pos] = '\0';
		goto out;
	}

	if (line[pos++] != '"')
		return FALSE;

	end = pos;

	while (end < len && line[end] != '"')
		end += 1;

	if (line[end] != '"')
		return FALSE;

	iter->buf[end] = '\0';

	/* Skip " */
	end += 1;

out:
	iter->line_pos = skip_to_next_field(line, end, len);

	if (str)
		*str = iter->buf + pos;

	return TRUE;
}

gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter,
		const guint8 **str, gint *length)
{
	unsigned int pos;
	unsigned int end;
	unsigned int len;
	char *line;
	char *bufpos;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	pos = iter->line_pos;
	bufpos = iter->buf + pos;

	/* Omitted string */
	if (line[pos] == ',') {
		end = pos;
		iter->buf[pos] = '\0';
		*length = 0;
		goto out;
	}

	if (line[pos] == '"')
		pos += 1;

	end = pos;

	while (end < len && g_ascii_isxdigit(line[end]))
		end += 1;

	if ((end - pos) & 1)
		return FALSE;

	*length = (end - pos) / 2;

	for (; pos < end; pos += 2)
		sscanf(line + pos, "%02hhx", bufpos++);

	if (line[end] == '"')
		end += 1;

out:
	iter->line_pos = skip_to_next_field(line, end, len);

	if (str)
		*str = (guint8 *) bufpos - *length;

	return TRUE;
}

gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number)
{
	int pos;
	int end;
	int len;
	int value = 0;
	char *line;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	pos = iter->line_pos;
	end = pos;

	while (line[end] >= '0' && line[end] <= '9') {
		value = value * 10 + (int)(line[end] - '0');
		end += 1;
	}

	if (pos == end)
		return FALSE;

	iter->line_pos = skip_to_next_field(line, end, len);

	if (number)
		*number = value;

	return TRUE;
}

gboolean g_at_result_iter_next_number_default(GAtResultIter *iter, gint dflt,
						gint *number)
{
	unsigned int pos;
	int len;
	char *line;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	pos = skip_to_next_field(line, iter->line_pos, len);

	if (pos != iter->line_pos) {
		iter->line_pos = pos;

		if (number)
			*number = dflt;

		return TRUE;
	}

	return g_at_result_iter_next_number(iter, number);
}

gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max)
{
	int pos;
	int end;
	int len;
	int low = 0;
	int high = 0;
	char *line;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	pos = iter->line_pos;

	while (pos < len && line[pos] == ' ')
		pos += 1;

	end = pos;

	while (line[end] >= '0' && line[end] <= '9') {
		low = low * 10 + (int)(line[end] - '0');
		end += 1;
	}

	if (pos == end)
		return FALSE;

	if (line[end] != '-') {
		high = low;
		goto out;
	}

	pos = end = end + 1;

	while (line[end] >= '0' && line[end] <= '9') {
		high = high * 10 + (int)(line[end] - '0');
		end += 1;
	}

	if (pos == end)
		return FALSE;

out:
	iter->line_pos = skip_to_next_field(line, end, len);

	if (min)
		*min = low;

	if (max)
		*max = high;

	return TRUE;
}

static gint skip_until(const char *line, int start, const char delim)
{
	int len = strlen(line);
	int i = start;

	while (i < len) {
		if (line[i] == delim)
			return i;

		if (line[i] == '\"') {
			i += 1;
			while (i < len && line[i] != '\"')
				i += 1;

			if (i < len)
				i += 1;

			continue;
		}

		if (line[i] != '(') {
			i += 1;
			continue;
		}

		i = skip_until(line, i+1, ')');

		if (i < len)
			i += 1;
	}

	return i;
}

gboolean g_at_result_iter_skip_next(GAtResultIter *iter)
{
	unsigned int skipped_to;
	char *line;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;

	skipped_to = skip_until(line, iter->line_pos, ',');

	if (skipped_to == iter->line_pos && line[skipped_to] != ',')
		return FALSE;

	iter->line_pos = skip_to_next_field(line, skipped_to, strlen(line));

	return TRUE;
}

gboolean g_at_result_iter_open_list(GAtResultIter *iter)
{
	char *line;
	unsigned int len;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	if (iter->line_pos >= len)
		return FALSE;

	if (line[iter->line_pos] != '(')
		return FALSE;

	iter->line_pos += 1;

	while (iter->line_pos < strlen(line) &&
		line[iter->line_pos] == ' ')
		iter->line_pos += 1;

	return TRUE;
}

gboolean g_at_result_iter_close_list(GAtResultIter *iter)
{
	char *line;
	unsigned int len;

	if (iter == NULL)
		return FALSE;

	if (iter->l == NULL)
		return FALSE;

	line = iter->l->data;
	len = strlen(line);

	if (iter->line_pos >= len)
		return FALSE;

	if (line[iter->line_pos] != ')')
		return FALSE;

	iter->line_pos += 1;

	iter->line_pos = skip_to_next_field(line, iter->line_pos, len);

	return TRUE;
}

const char *g_at_result_final_response(GAtResult *result)
{
	if (result == NULL)
		return NULL;

	return result->final_or_pdu;
}

const char *g_at_result_pdu(GAtResult *result)
{
	if (result == NULL)
		return NULL;

	return result->final_or_pdu;
}

gint g_at_result_num_response_lines(GAtResult *result)
{
	if (result == NULL)
		return 0;

	if (result->lines == NULL)
		return 0;

	return g_slist_length(result->lines);
}
