/*
 * Copyright (C) 2012-2015 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 .
 */
#ifndef __EXPRESSIONS_H
#define __EXPRESSIONS_H
#include 
#include "undo.h"
#include "error.h"
namespace SciTECO {
template 
class ValueStack {
	class UndoTokenPush : public UndoTokenWithSize {
		/*
		 * FIXME: saving the UndoStack for each undo taken
		 * wastes a lot of memory
		 */
		ValueStack *stack;
		Type	value;
		guint	index;
	public:
		UndoTokenPush(ValueStack *_stack,
			      Type _value, guint _index = 0)
			     : stack(_stack), value(_value), index(_index) {}
		void
		run(void)
		{
			stack->push(value, index);
		}
	};
	class UndoTokenPop : public UndoTokenWithSize {
		ValueStack *stack;
		guint index;
	public:
		UndoTokenPop(ValueStack *_stack, guint _index = 0)
			    : stack(_stack), index(_index) {}
		void
		run(void)
		{
			stack->pop(index);
		}
	};
	/** Beginning of stack area */
	Type *stack;
	/** End of stack area */
	Type *stack_top;
	/** Pointer to top element on stack */
	Type *sp;
public:
	ValueStack(gsize size = 1024)
	{
		stack = new Type[size];
		/* stack grows to smaller addresses */
		sp = stack_top = stack+size;
	}
	~ValueStack()
	{
		delete[] stack;
	}
	inline guint
	items(void)
	{
		return stack_top - sp;
	}
	inline Type &
	push(Type value, guint index = 0)
	{
		if (G_UNLIKELY(sp == stack))
			throw Error("Stack overflow");
		/* reserve space for new element */
		sp--;
		g_assert(items() > index);
		/* move away elements after index (index > 0) */
		for (guint i = 0; i < index; i++)
			sp[i] = sp[i+1];
		return sp[index] = value;
	}
	inline void
	undo_push(Type value, guint index = 0)
	{
		undo.push(new UndoTokenPush(this, value, index));
	}
	inline Type
	pop(guint index = 0)
	{
		/* peek() already asserts */
		Type v = peek(index);
		/* elements after index are moved to index (index > 0) */
		while (index--)
			sp[index+1] = sp[index];
		/* free space of element to pop */
		sp++;
		return v;
	}
	inline void
	undo_pop(guint index = 0)
	{
		undo.push(new UndoTokenPop(this, index));
	}
	inline Type &
	peek(guint index = 0)
	{
		g_assert(items() > index);
		return sp[index];
	}
	inline void
	clear(void)
	{
		sp = stack_top;
	}
};
/*
 * Arithmetic expression stacks
 */
extern class Expressions {
public:
	/* reflects also operator precedence */
	enum Operator {
		OP_NIL = 0,
		OP_POW,		// ^*
		OP_MUL,		// *
		OP_DIV,		// /
		OP_MOD,		// ^/
		OP_ADD,		// +
		OP_SUB,		// -
		OP_AND,		// &
		OP_XOR,		// ^#
		OP_OR,		// #
				// pseudo operators:
		OP_NEW,
		OP_BRACE,
		OP_LOOP,
		OP_NUMBER
	};
private:
	ValueStack	numbers;
	ValueStack	operators;
public:
	Expressions() : num_sign(1), radix(10) {}
	gint num_sign;
	inline void
	set_num_sign(gint sign)
	{
		undo.push_var(num_sign) = sign;
	}
	gint radix;
	inline void
	set_radix(gint r)
	{
		undo.push_var(radix) = r;
	}
	tecoInt push(tecoInt number);
	/**
	 * Push characters of a C-string.
	 * Could be overloaded on push(tecoInt)
	 * but this confuses GCC.
	 */
	inline void
	push_str(const gchar *str)
	{
		while (*str)
			push(*str++);
	}
	inline tecoInt
	peek_num(guint index = 0)
	{
		return numbers.peek(index);
	}
	tecoInt pop_num(guint index = 0);
	tecoInt pop_num_calc(guint index, tecoInt imply);
	inline tecoInt
	pop_num_calc(guint index = 0)
	{
		return pop_num_calc(index, num_sign);
	}
	tecoInt add_digit(gchar digit);
	Operator push(Operator op);
	Operator push_calc(Operator op);
	inline Operator
	peek_op(guint index = 0)
	{
		return operators.peek(index);
	}
	Operator pop_op(guint index = 0);
	void eval(bool pop_brace = false);
	guint args(void);
	void discard_args(void);
	gint find_op(Operator op);
	inline void
	clear(void)
	{
		numbers.clear();
		operators.clear();
	}
	const gchar *format(tecoInt number);
private:
	void calc(void);
	gint first_op(void);
} expressions;
} /* namespace SciTECO */
#endif