#pragma once

// [config-parser.h]: configuration file parser
//
// -------------------------------------------------------------------------------------------- //

#include "cfg-value.h"
#include "cfg-cmd.h"
#include "cfg-vec.h"

#include "lexeme-list.h"
#include "mem-buffer.h"

#include <stack>

namespace scm
{
	// call back default
	// -------------------------------------------------------------------------------------------- //
	class parserCallBack {
	public:

		virtual bool call_back(const cfgCommand& cmd) { return true; }

		parserCallBack() {}
		virtual ~parserCallBack() {}
	};
	// -------------------------------------------------------------------------------------------- //

	class ConfigParser {
	public:

		// -------------------------------------------------------------------------------------------- //
		ConfigParser();
		~ConfigParser();
		// -------------------------------------------------------------------------------------------- //

		// run
		// -------------------------------------------------------------------------------------------- //
		bool run(const char* filename);
		bool run(const char* filename, parserCallBack& pcb);
		// -------------------------------------------------------------------------------------------- //

		// get & check calls
		// -------------------------------------------------------------------------------------------- //
		bool is_varname(const char* name) const;
		const cfgVector get_variable(const int idx) const;
		const cfgVector get_variable(const char* name) const;

		bool get_value(const char* name, int* value) const;
		bool get_value(const char* name, float* value) const;
		bool get_value(const char* name, double* value) const;
		bool get_value(const char* name, long double* value) const;

		bool get_value(const char* name, char** value) const;
		bool get_value(const char* name, std::string& value) const;

		bool get_value(const char* name, bool* value) const;
		// -------------------------------------------------------------------------------------------- //

		// adding variable
		// -------------------------------------------------------------------------------------------- //
		bool add(const cfgVector& rvalue);

		bool is_valid_name(const char* name) const;
		// -------------------------------------------------------------------------------------------- //

		// print
		// -------------------------------------------------------------------------------------------- //
		void print() const;
		// -------------------------------------------------------------------------------------------- //

	private:
		// datatypes
		// -------------------------------------------------------------------------------------------- //

		// --- helper struct to control parsing state
		// -------------------------------------------------------------------------------------------- //
		struct parserState {

			parserState();
			parserState(const parserState& state);
			~parserState();

			void truncate_name_space();
			void append_name_space(const char* name);

			const char* get_global_name(const char* varname);

			int idx;			// lexeme index
			char *name_space;	// current namespace

		private:	// allocation data
			int nalloc;
			static const int c_alloc_init = 64;

			char *name_buf;
			int name_buf_nalloc;
		};
		// -------------------------------------------------------------------------------------------- //

	private:
		// processing (private)
		// -------------------------------------------------------------------------------------------- //

		// --- expression rpn evaluation
		bool get_rpn(
			mem_buffer<int>& rpn,
			mem_buffer<int>& rpn_key,
			const LexemeList& lexeme_list,
			const int lex_beg, const int lex_end) const;

		bool evaluate_rpn(
			std::stack<cfgVector>& dyn_expr,
			const mem_buffer< int >& rpn,
			const mem_buffer<int>& rpn_key,
			const int rpn_beg, const int rpn_end,
			const LexemeList& lexeme_list,
			const char* name_space,
			const bool exe_cntrl) const;
		bool evaluate_rpn(
			cfgVector& res,
			const mem_buffer< int >& rpn,
			const mem_buffer<int>& rpn_key,
			const LexemeList& lexeme_list,
			const char* name_space,
			const bool exe_cntrl) const;
		// -------------------------------------------------------------------------------------------- //

		// --- command setup 
		bool set_command(
			cfgCommand& cmd,
			const mem_buffer< int >& rpn,
			const mem_buffer< int >& fun_narg,
			const LexemeList& lexeme_list,
			const bool exe_cntrl) const;
		// -------------------------------------------------------------------------------------------- //

		// --- syntax analysis
		bool parse_syntax(const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);

		bool parse_block(
			parserState& state, const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);
		bool parse_command(
			parserState& state, const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);
		bool parse_if_statement(
			parserState& state, const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);
		bool parse_while_statement(
			parserState& state, const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);
		bool parse_name_space(
			parserState& state, const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);
		bool parse_assignment(
			parserState& state, const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);
		bool parse_address_assignment(
			parserState& state, const LexemeList& lexeme_list, const bool exe_cntrl, parserCallBack& pcb);
		// -------------------------------------------------------------------------------------------- //

		// --- add variable (no additional checks) 
		bool add_unsafe(const cfgVector& rvalue);
		// -------------------------------------------------------------------------------------------- //

	private:
		// static (private)
		// -------------------------------------------------------------------------------------------- //

		// +,-		[priority=1, assoc.=left]
		// *,/,%	[priority=2, assoc.=left]
		// +,-		[priority=3, assoc.=right]
		// ^		[priority=4, assoc.=right]
		static int operator_priority(const Lexeme::TYPE type);
		static bool operator_priority_lt(
			const Lexeme::TYPE typeA, const Lexeme::TYPE typeB);

		static int function_nargs(const Lexeme::TYPE type);

		static bool is_operator_binary(const Lexeme::TYPE type);
		static bool is_operator_unary(const Lexeme::TYPE type);
		static bool is_operator_assoc_left(const Lexeme::TYPE type);
		static bool is_operator_assoc_right(const Lexeme::TYPE type);

	private:
		// data (private)
		// -------------------------------------------------------------------------------------------- //

		int nvars;
		int nalloc_vars;
		static const int c_alloc_vars_init = 64;
		cfgVector *var;
	};
	// -------------------------------------------------------------------------------------------- //
}