/*
 * filter-visitor-xml.c
 *
 * LTTng filter XML pretty printer visitor
 *
 * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
 *
 * SPDX-License-Identifier: LGPL-2.1-only
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>
#include "filter-ast.h"
#include "filter-parser.h"

#include <common/compat/errno.h>
#include <common/macros.h>

#define fprintf_dbg(fd, fmt, args...)	fprintf(fd, "%s: " fmt, __func__, ## args)

static
int recursive_visit_print(struct filter_node *node, FILE *stream, int indent);

static
void print_tabs(FILE *fd, int depth)
{
	int i;

	for (i = 0; i < depth; i++)
		fprintf(fd, "\t");
}

static
int recursive_visit_print_expression(struct filter_node *node,
		FILE *stream, int indent)
{
	struct filter_node *iter_node;

	if (!node) {
		fprintf(stderr, "[error] %s: NULL child\n", __func__);
		return -EINVAL;
	}
	switch (node->u.expression.type) {
	case AST_EXP_UNKNOWN:
	default:
		fprintf(stderr, "[error] %s: unknown expression\n", __func__);
		return -EINVAL;
	case AST_EXP_STRING:
		print_tabs(stream, indent);
		fprintf(stream, "<string value=\"%s\"/>\n",
			node->u.expression.u.string);
		break;
	case AST_EXP_CONSTANT:
		print_tabs(stream, indent);
		fprintf(stream, "<constant value=\"%" PRIu64 "\"/>\n",
			node->u.expression.u.constant);
		break;
	case AST_EXP_FLOAT_CONSTANT:
		print_tabs(stream, indent);
		fprintf(stream, "<float_constant value=\"%lg\"/>\n",
			node->u.expression.u.float_constant);
		break;
	case AST_EXP_IDENTIFIER:		/* fall-through */
	case AST_EXP_GLOBAL_IDENTIFIER:
		print_tabs(stream, indent);
		fprintf(stream, "<%s value=\"%s\"/>\n",
			node->u.expression.type == AST_EXP_IDENTIFIER ?
				"identifier" : "global_identifier",
			node->u.expression.u.identifier);
		iter_node = node->u.expression.next;
		while (iter_node) {
			print_tabs(stream, indent);
			fprintf(stream, "<bracket>\n");
			if (recursive_visit_print_expression(iter_node,
					stream, indent + 1)) {
				return -EINVAL;
			}
			print_tabs(stream, indent);
			fprintf(stream, "</bracket>\n");
			iter_node = iter_node->u.expression.next;

		}
		break;
	case AST_EXP_NESTED:
		return recursive_visit_print(node->u.expression.u.child,
				stream, indent + 1);
	}
	return 0;
}


static
int recursive_visit_print(struct filter_node *node, FILE *stream, int indent)
{
	int ret;

	if (!node) {
		fprintf(stderr, "[error] %s: NULL child\n", __func__);
		return -EINVAL;
	}
	switch (node->type) {
	case NODE_UNKNOWN:
	default:
		fprintf(stderr, "[error] %s: unknown node type\n", __func__);
		return -EINVAL;
	case NODE_ROOT:
		print_tabs(stream, indent);
		fprintf(stream, "<root>\n");
		ret = recursive_visit_print(node->u.root.child, stream,
					indent + 1);
		print_tabs(stream, indent);
		fprintf(stream, "</root>\n");
		return ret;
	case NODE_EXPRESSION:
		print_tabs(stream, indent);
		fprintf(stream, "<expression>\n");
		ret = recursive_visit_print_expression(node, stream,
					indent + 1);
		print_tabs(stream, indent);
		fprintf(stream, "</expression>\n");
		return ret;
	case NODE_OP:
		print_tabs(stream, indent);
		fprintf(stream, "<op type=");
		switch (node->u.op.type) {
		case AST_OP_UNKNOWN:
		default:
			fprintf(stderr, "[error] %s: unknown op\n", __func__);
			return -EINVAL;
		case AST_OP_MUL:
			fprintf(stream, "\"*\"");
			break;
		case AST_OP_DIV:
			fprintf(stream, "\"/\"");
			break;
		case AST_OP_MOD:
			fprintf(stream, "\"%%\"");
			break;
		case AST_OP_PLUS:
			fprintf(stream, "\"+\"");
			break;
		case AST_OP_MINUS:
			fprintf(stream, "\"-\"");
			break;
		case AST_OP_BIT_RSHIFT:
			fprintf(stream, "\">>\"");
			break;
		case AST_OP_BIT_LSHIFT:
			fprintf(stream, "\"<<\"");
			break;
		case AST_OP_AND:
			fprintf(stream, "\"&&\"");
			break;
		case AST_OP_OR:
			fprintf(stream, "\"||\"");
			break;
		case AST_OP_BIT_AND:
			fprintf(stream, "\"&\"");
			break;
		case AST_OP_BIT_OR:
			fprintf(stream, "\"|\"");
			break;
		case AST_OP_BIT_XOR:
			fprintf(stream, "\"^\"");
			break;

		case AST_OP_EQ:
			fprintf(stream, "\"==\"");
			break;
		case AST_OP_NE:
			fprintf(stream, "\"!=\"");
			break;
		case AST_OP_GT:
			fprintf(stream, "\">\"");
			break;
		case AST_OP_LT:
			fprintf(stream, "\"<\"");
			break;
		case AST_OP_GE:
			fprintf(stream, "\">=\"");
			break;
		case AST_OP_LE:
			fprintf(stream, "\"<=\"");
			break;
		}
		fprintf(stream, ">\n");
		ret = recursive_visit_print(node->u.op.lchild,
					stream, indent + 1);
		if (ret)
			return ret;
		ret = recursive_visit_print(node->u.op.rchild,
					stream, indent + 1);
		if (ret)
			return ret;
		print_tabs(stream, indent);
		fprintf(stream, "</op>\n");
		return ret;
	case NODE_UNARY_OP:
		print_tabs(stream, indent);
		fprintf(stream, "<unary_op type=");
		switch (node->u.unary_op.type) {
		case AST_UNARY_UNKNOWN:
		default:
			fprintf(stderr, "[error] %s: unknown unary_op\n", __func__);
			return -EINVAL;
		case AST_UNARY_PLUS:
			fprintf(stream, "\"+\"");
			break;
		case AST_UNARY_MINUS:
			fprintf(stream, "\"-\"");
			break;
		case AST_UNARY_NOT:
			fprintf(stream, "\"!\"");
			break;
		case AST_UNARY_BIT_NOT:
			fprintf(stream, "\"~\"");
			break;
		}
		fprintf(stream, ">\n");
		ret = recursive_visit_print(node->u.unary_op.child,
					stream, indent + 1);
		print_tabs(stream, indent);
		fprintf(stream, "</unary_op>\n");
		return ret;
	}
	return 0;
}

LTTNG_HIDDEN
int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
			int indent)
{
	return recursive_visit_print(&ctx->ast->root, stream, indent);
}
