/*
 * Copyright (C) 2012-2013 Robin Haberkorn
 *
 * 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 3 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, see .
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include 
#include 
#include "sciteco.h"
#include "expressions.h"
#include "parser.h"
#include "undo.h"
#include "goto.h"
namespace States {
	StateLabel	label;
	StateGotoCmd	gotocmd;
}
namespace Goto {
	GotoTable *table = NULL;
	gchar *skip_label = NULL;
}
gint
GotoTable::remove(gchar *name)
{
	gint existing_pc = -1;
	Label label(name);
	Label *existing = (Label *)RBTree::find(&label);
	if (existing) {
		existing_pc = existing->pc;
		RBTree::remove(existing);
		delete existing;
	}
	return existing_pc;
}
gint
GotoTable::find(gchar *name)
{
	Label label(name);
	Label *existing = (Label *)RBTree::find(&label);
	return existing ? existing->pc : -1;
}
gint
GotoTable::set(gchar *name, gint pc)
{
	if (pc < 0)
		return remove(name);
	Label *label = new Label(name, pc);
	Label *existing;
	gint existing_pc = -1;
	existing = (Label *)RBTree::find(label);
	if (existing) {
		existing_pc = existing->pc;
		g_free(existing->name);
		existing->name = label->name;
		existing->pc = label->pc;
		label->name = NULL;
		delete label;
	} else {
		RBTree::insert(label);
	}
#ifdef DEBUG
	dump();
#endif
	return existing_pc;
}
#ifdef DEBUG
void
GotoTable::dump(void)
{
	for (Label *cur = (Label *)RBTree::min();
	     cur != NULL;
	     cur = (Label *)cur->next())
		g_printf("table[\"%s\"] = %d\n", cur->name, cur->pc);
	g_printf("---END---\n");
}
#endif
/*
 * Command states
 */
StateLabel::StateLabel() : State()
{
	transitions['\0'] = this;
}
State *
StateLabel::custom(gchar chr) throw (Error)
{
	if (chr == '!') {
		Goto::table->undo_set(strings[0],
				      Goto::table->set(strings[0], macro_pc));
		if (!g_strcmp0(strings[0], Goto::skip_label)) {
			g_free(undo.push_str(Goto::skip_label));
			Goto::skip_label = NULL;
			undo.push_var(mode) = MODE_NORMAL;
		}
		g_free(undo.push_str(strings[0]));
		strings[0] = NULL;
		return &States::start;
	}
	String::append(undo.push_str(strings[0]), chr);
	return this;
}
State *
StateGotoCmd::done(const gchar *str) throw (Error)
{
	gint64 value;
	gchar **labels;
	BEGIN_EXEC(&States::start);
	value = expressions.pop_num_calc();
	labels = g_strsplit(str, ",", -1);
	if (value > 0 && value <= g_strv_length(labels) && *labels[value-1]) {
		gint pc = Goto::table->find(labels[value-1]);
		if (pc >= 0) {
			macro_pc = pc;
		} else {
			/* skip till label is defined */
			undo.push_str(Goto::skip_label);
			Goto::skip_label = g_strdup(labels[value-1]);
			undo.push_var(mode) = MODE_PARSE_ONLY_GOTO;
		}
	}
	g_strfreev(labels);
	return &States::start;
}