/*
    c99tree - parse tree output layer, output format: graphviz dot
    Copyright (C) 2015  Tibor Palinkas

    This program 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.

    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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tree.h"
#include "tree_print.h"
#include "c99tree.h"
#include "escaping.h"

typedef struct ctx_s {
	int clust_open; /* whether a cluster is open */
	int in_token;   /* whether we are printing the token tree */

	/* post-string: print these right before closing the graph */
	int post_used, post_alloced;
	char *post;
} ctx_t;

enum Nhilight {
	NH_ARG   = 1,  /* green 1 */
	NH_DECL  = 2,  /* green 2 */
	NH_EXPR  = 4,  /* blue 1 */
	NH_CALL  = 8,  /* blue 2 */
	NH_CONST = 16  /* red */
};

static void Ncolor(node_t *node, int context)
{
	node_t *ch;

	switch(node->type) {
		case T_ARGLIST:
		case T_ARG:
			context |= NH_ARG;
			break;
		case T_DECL:
			context |= NH_DECL;
			break;
		case T_EXPRBRACKET:
		case T_EXPRLIST:
		case T_OP:
			context |= NH_EXPR;
			break;
		case T_CALL:
			context |= NH_CALL;
			break;
		case T_ICONST:
		case T_FCONST:
		case T_SCONST:
		case T_SCONSTLIST:
			context |= NH_CONST;
			break;
		default:;
	}
	node->print_data = context;
	for(ch = node->children; ch != NULL; ch = ch->next)
		Ncolor(ch, context);
}

static char Ncolor_buff[32];
const char *Ncolor_str(int context)
{
	int clr;

	clr = 0xDDDDDD;
	if (context & NH_ARG)  clr ^= 0x440044;
	if (context & NH_DECL) clr ^= 0x880088;
	if (context & NH_EXPR) clr ^= 0x888800;
	if (context & NH_CALL) clr ^= 0x444400;
	if (context & NH_CONST) clr = 0xFF0000;
	sprintf(Ncolor_buff, "#%05X", clr);
	return Ncolor_buff;
}

static ctx_t *ctx_check(void **nprint_ctx)
{
	ctx_t *ctx;

	if (*nprint_ctx != NULL)
		return (ctx_t *)*nprint_ctx;

	ctx = calloc(sizeof(ctx_t), 1);
	*nprint_ctx = ctx;
	return ctx;
}

void ctx_close(ctx_t *ctx)
{
	if (ctx->post != NULL)
		free(ctx->post);
	free(ctx);
}

static void cluster_close(ctx_t *ctx)
{
	if (ctx->clust_open) {
		printf("\t}\n");
		ctx->clust_open = 0;
	}
}

static void cluster_open(ctx_t *ctx, char *name, int in_token)
{
	cluster_close(ctx);
	printf("\tsubgraph \"cluster_%s\" {\n", name);
	printf("\t\trankdir = \"TB\";\n");
	ctx->clust_open = 1;
	ctx->in_token = in_token;
}

#define post_printf(ctx, ...) \
do { \
	char buff__[1024]; \
	int len__; \
	len__ = sprintf(buff__, __VA_ARGS__); \
	if (ctx->post_used + len__ + 1 >= ctx->post_alloced) { \
		ctx->post_alloced = ctx->post_used + len__ + 4096; \
		ctx->post = realloc(ctx->post, ctx->post_alloced); \
	} \
	memcpy(ctx->post + ctx->post_used, buff__, len__+1); \
	ctx->post_used += len__; \
} while(0)

int Nprint_dot(void **nprint_ctx, const char *prefix, const node_t *node, print_state_t state)
{
	char *tmp, *style;
	node_t *ch;
	ctx_t *ctx;

	ctx = ctx_check(nprint_ctx);

	switch(state) {
		case PS_OPENING:
			style = "filled";
			if (node->type == T_GRAMMAR_TREE) {
				cluster_open(ctx, "grammar", 0);
			}
			else if (node->type == T_TOKEN_TREE) {
				cluster_open(ctx, "token", 1);
				style = "invis";
			}

			tmp = escape(node->name);
			printf("\t\t\"%d\" [label=\"%s\\n%s\\n%s\", color=\"%s\", style=\"%s\"]\n", node->uid, node_type_names[node->type], tmp, loc_print(node), Ncolor_str(node->print_data), style);
			free(tmp);

			for(ch = node->children; ch != NULL; ch = ch->next) {
				post_printf(ctx, "\t\"%d\" -> \"%d\"", node->uid, ch->uid);
				if (node->type == T_TOKEN_TREE) {
					/* token_tree should point to its first child only to flatten that tree */
					post_printf(ctx, "[style=invis]\n");
					break;
				}
				post_printf(ctx, "\n");
			}

			if (node->type == T_TOKEN_TREE) {
				int line = -1, last_line = -1, pbind;
				for(ch = node->children; ch != NULL; ch = ch->next) {
						if ((*ch->name == '\n') || (line < 0)) {
						if (line >= 0)
							printf("\t\t}}\n"); /* close the line subgraph */
						last_line = line;
						line = ch->loc.cpp_line;
						printf("\t\tsubgraph cluster_line_%d { style=invis; {\n", line);
						printf("\t\t\trank = \"same\";\n");
						printf("\t\t\t\"l_%d\" [label=\"%s\\nline %d\", color=\"green\", style=\"filled\"]\n", line, node_type_names[node->type], line);
						if (last_line > 0) {
							post_printf(ctx, "\t\"l_%d\" -> \"l_%d_sp\" [style=invis]\n", last_line, line);
							post_printf(ctx, "\t\"l_%d_sp\" -> \"l_%d\" [style=invis]\n", line, line);
							post_printf(ctx, "\t\"l_%d_sp\" [label=\"\" style=invis]\n", line);
						}
						pbind = 0;
					}

					/* order of token nodes within the line */
					if ((ch->next != NULL) && (*ch->next->name != '\n'))
						post_printf(ctx, "\t\"%d\" -> \"%d\" [style=invis]\n", ch->uid, ch->next->uid);

					/* binding to the parent line */
					if (pbind == 0) {
						post_printf(ctx, "\t\"l_%d\" -> \"%d\" [style=invis]\n", line, ch->uid);
						pbind = 1;
					}

					printf("\t\t\t\"%d\"\n", ch->uid);
				}
				if (line >= 0)
					printf("\t\t}\n"); /* close the line subgraph */
			}


			if (node->loc.twin_parent != NULL)
				post_printf(ctx, "\t\"%d\" -> \"%d\" [ color=\"green\" constraint=\"false\"]\n", node->uid, node->loc.twin_parent->uid);
			break;
		case PS_HEAD:
			printf("# location_format=%s\n", loc_format);
			printf("digraph FILE {\n");
			for(ch = (node_t *)node; ch != NULL; ch = ch->next)
				Ncolor(ch, 0);
			break;
		case PS_TAIL:
			cluster_close(ctx);
			printf("%s", ctx->post);
			printf("}\n");
			ctx_close(ctx);
			break;
		case PS_CLOSING:
			break;
	}
	return 0;
}
