/*
 * Copyright (C) 2012-2014 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 __PARSER_H
#define __PARSER_H
#include 
#include "undo.h"
#include "error.h"
#include "sciteco.h"
namespace SciTECO {
/* TECO uses only lower 7 bits for commands */
#define MAX_TRANSITIONS	127
class State {
protected:
	/* static transitions */
	State *transitions[MAX_TRANSITIONS];
	inline void
	init(const gchar *chars, State &state)
	{
		while (*chars)
			transitions[(int)*chars++] = &state;
	}
	inline void
	init(const gchar *chars)
	{
		init(chars, *this);
	}
public:
	State();
	static void input(gchar chr);
	State *get_next_state(gchar chr);
protected:
	static bool eval_colon(void);
	virtual State *
	custom(gchar chr)
	{
		throw SyntaxError(chr);
		return NULL;
	}
};
template 
class MicroStateMachine {
protected:
	/* label pointers */
	typedef const void *MicroState;
	const MicroState StateStart;
#define MICROSTATE_START G_STMT_START {	\
	if (this->state != StateStart)		\
		goto *this->state;		\
} G_STMT_END
	MicroState state;
#ifdef EMSCRIPTEN
	/* FIXME: Shouldn't be required! */
	__attribute__((noinline))
#else
	inline
#endif
	void
	set(MicroState next)
	{
		if (next != state)
			undo.push_var(state) = next;
	}
public:
	MicroStateMachine() : StateStart(NULL), state(StateStart) {}
	virtual ~MicroStateMachine() {}
	virtual inline void
	reset(void)
	{
		set(StateStart);
	}
	virtual Type input(gchar chr) = 0;
};
/* avoid circular dependency on qregisters.h */
class QRegSpecMachine;
class StringBuildingMachine : public MicroStateMachine {
	QRegSpecMachine *qregspec_machine;
	enum Mode {
		MODE_NORMAL,
		MODE_UPPER,
		MODE_LOWER
	} mode;
	bool toctl;
public:
	StringBuildingMachine() : MicroStateMachine(),
				  qregspec_machine(NULL),
				  mode(MODE_NORMAL), toctl(false) {}
	~StringBuildingMachine();
	void reset(void);
	gchar *input(gchar chr);
};
/*
 * Super-class for states accepting string arguments
 * Opaquely cares about alternative-escape characters,
 * string building commands and accumulation into a string
 */
class StateExpectString : public State {
	StringBuildingMachine machine;
	gint nesting;
	bool string_building;
	bool last;
public:
	StateExpectString(bool _building = true, bool _last = true)
			 : State(), nesting(1),
			   string_building(_building), last(_last) {}
private:
	State *custom(gchar chr);
protected:
	virtual void initial(void) {}
	virtual void process(const gchar *str, gint new_chars) {}
	virtual State *done(const gchar *str) = 0;
};
class StateExpectFile : public StateExpectString {
public:
	StateExpectFile(bool _building = true, bool _last = true)
		       : StateExpectString(_building, _last) {}
};
class StateStart : public State {
public:
	StateStart();
private:
	void insert_integer(tecoInt v);
	tecoInt read_integer(void);
	tecoBool move_chars(tecoInt n);
	tecoBool move_lines(tecoInt n);
	tecoBool delete_words(tecoInt n);
	State *custom(gchar chr);
};
class StateControl : public State {
public:
	StateControl();
private:
	State *custom(gchar chr);
};
class StateASCII : public State {
public:
	StateASCII();
private:
	State *custom(gchar chr);
};
class StateFCommand : public State {
public:
	StateFCommand();
private:
	State *custom(gchar chr);
};
class StateCondCommand : public State {
public:
	StateCondCommand();
private:
	State *custom(gchar chr);
};
class StateECommand : public State {
public:
	StateECommand();
private:
	State *custom(gchar chr);
};
class StateScintilla_symbols : public StateExpectString {
public:
	StateScintilla_symbols() : StateExpectString(true, false) {}
private:
	State *done(const gchar *str);
};
class StateScintilla_lParam : public StateExpectString {
private:
	State *done(const gchar *str);
};
/*
 * also serves as base class for replace-insertion states
 */
class StateInsert : public StateExpectString {
protected:
	void initial(void);
	void process(const gchar *str, gint new_chars);
	State *done(const gchar *str);
};
namespace States {
	extern StateStart 		start;
	extern StateControl		control;
	extern StateASCII		ascii;
	extern StateFCommand		fcommand;
	extern StateCondCommand		condcommand;
	extern StateECommand		ecommand;
	extern StateScintilla_symbols	scintilla_symbols;
	extern StateScintilla_lParam	scintilla_lparam;
	extern StateInsert		insert;
	extern State *current;
	static inline bool
	is_string()
	{
		return dynamic_cast(current);
	}
	static inline bool
	is_file()
	{
		return dynamic_cast(current);
	}
}
extern enum Mode {
	MODE_NORMAL = 0,
	MODE_PARSE_ONLY_GOTO,
	MODE_PARSE_ONLY_LOOP,
	MODE_PARSE_ONLY_COND
} mode;
#define BEGIN_EXEC(STATE) G_STMT_START {	\
	if (mode > MODE_NORMAL)			\
		return STATE;			\
} G_STMT_END
extern gint macro_pc;
extern gchar *strings[2];
extern gchar escape_char;
namespace Execute {
	void step(const gchar *macro, gint stop_pos)
		 throw (Error, ReplaceCmdline);
	void macro(const gchar *macro, bool locals = true);
	void file(const gchar *filename, bool locals = true);
}
} /* namespace SciTECO */
#endif