/*
	Copyright (C) 2008-2026 Mark Tyler

	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 in the file COPYING.
*/

#ifndef CED_EVAL_H_
#define CED_EVAL_H_



#include "ced.h"



#ifndef DEBUG
#pragma GCC visibility push ( hidden )
#endif



///	DECLARATIONS	--------------------------------------------------------

#ifdef __cplusplus
extern "C" {
#endif

/// C API ----------------------------------------------------------------------



//#define CED_USE_BISON_PARSER



enum
{
	CED_ARGSET_VARIABLE		= -1,

	CED_ARGSET_NONE			= 0,

	CED_ARGSET_VOID,
	CED_ARGSET_NUM,
	CED_ARGSET_NUM_NUM,
	CED_ARGSET_NUM_NUM_NUM,
	CED_ARGSET_CELLRANGE,
	CED_ARGSET_CELLRANGE_NUM,
	CED_ARGSET_NUM_CELLRANGE_NUM,
	CED_ARGSET_CELLREF_NUM_NUM,
	CED_ARGSET_CELLRANGE_STR_NUM,
	CED_ARGSET_CELLRANGE_STR_NUM_CELLREF,
	CED_ARGSET_CELLREF_CELLRANGE_NUM,

	CED_ARGSET_TOTAL
};

enum
{
	CED_FUNC_FLAG_VOLATILE		= 1 << 0,

	CED_FUNC_FLAG_ALL		= ~0
};

enum
{
	CED_FARG_TYPE_NONE		= 0,

	CED_FARG_TYPE_NUM,
	CED_FARG_TYPE_CELLRANGE,
	CED_FARG_TYPE_CELLREF,
	CED_FARG_TYPE_STRING,

	CED_FARG_TYPE_TOTAL
};



typedef struct CedFunc		CedFunc;
typedef struct CedFuncArg	CedFuncArg;
typedef struct CedFuncInfo	CedFuncInfo;
typedef struct CedFuncState	CedFuncState;
typedef struct PArgSet		PArgSet;
typedef struct PCellRange	PCellRange;
typedef struct PCellRef		PCellRef;



typedef int (* CedFuncCall) (		// Function callback
	CedFuncState	* funcs		// Function state
	);
	// 0 = success
	// 1 = fail



struct CedFuncArg
{
	int		type;		// CED_FARG_TYPE_*

	CedSheet	* sheet;

	union
	{
	double		val;
	CedCellRef	cellref[2];	// [0] for cellref, both for cellrange
	char	const	* str;
	} u;
};

struct CedFuncState
{
	CedFunc const	* func;		// Function being actioned
	CedParser	* parser;	// Parser state
	double		* result;	// Populated by caller: Result of
					// function is put here
	CedFuncArg	* arg;		// Points to array of
					// [CED_FUNC_ARG_MAX + 1] or NULL for
					// no arguments
};

struct CedFunc
{
	int		const	type;
	CedFuncCall	const	call;
	int		const	flag;
};

struct CedFuncInfo
{
	char		const	* name;
	CedFunc			func;
	char		const	* help;
};

struct PCellRange
{
	CedSheet	* sheet;
	CedCellRef	cellref[2];
};

struct PCellRef
{
	CedSheet	* sheet;
	CedCellRef	cellref;
};

struct PArgSet
{
	int		tot;
	CedFuncArg	arg[CED_FUNC_ARG_MAX + 1];
};



int ced_func_exe (			// Execute a function
	CedFuncState	* state
	);

CedFunc const * ced_func_get (		// Get func structure for a function
	char	const	* text		// Function name
	);

CedFuncInfo const * ced_func_info (
	int		index
	);

int cedparser_get_cell_value (		// Get cell value from spreadsheet
	CedParser	* state,
	PCellRef const	* cref,
	double		* result
	);
	// 0 = success

int pargset_arg_add (
	PArgSet		* argset,
	CedFuncArg	* arg
	);
	// 0 = success

int ced_strtosheetref (
	char	const	* const	input,
				// Input string to parse NUL terminated
				// & ' character = start/finish, e.g.
				// 'Sheet 12'
				// '' = ' : 'Sheet '' 12' = Sheet ' 12
	CedSheet	** const result,
				// Put result here
	char	const *	* const next,
				// Next un-parsed character is put here,
				// (NULL = don't use
	CedBook		* const book
				// The book to search for this sheet
				// reference
	);



#ifndef	CED_USE_BISON_PARSER



int ced_parse_text (
	char	const	* text,
	CedParser	* state
	);



#endif	// CED_USE_BISON_PARSER



/// C++ API --------------------------------------------------------------------

#ifdef __cplusplus
}



#ifndef	CED_USE_BISON_PARSER



namespace mtCED
{



class Lexer
{
public:
	enum
	{
		// Tokens/Errors/etc from the Lexer
		// Whitespace between tokens is skipped and not reported

		TK_END			= 0,	// End of input

		TK_NUMBER,		// 1, -1.1, -1.1e+12, -1.1e-12, etc

		TK_NAME_STRING,		// Function or variable name
		TK_QUOTE_STRING,	// "hello world", "<"

		TK_OP_ADD,		// +
		TK_OP_SUB,		// -
		TK_OP_MULT,		// *
		TK_OP_DIV,		// /
		TK_OP_POWER,		// ^

		TK_CMP_LT,		// <	=> 1,0
		TK_CMP_LTE,		// <=	=> 1,0
		TK_CMP_GT,		// >	=> 1,0
		TK_CMP_GTE,		// >=	=> 1,0
		TK_CMP_EQ,		// =	=> 1,0
		TK_CMP_NEQ,		// !=	=> 1,0
		TK_CMP_LEG,		// <=>	=> -1,0,1

		TK_PAREN_OPEN,		// (
		TK_PAREN_CLOSE,		// )
		TK_HASH,		// #
		TK_COLON,		// :

		TK_CELLREF,		// r1c1, r[1]c[1]
		TK_SHEETREF,		// 'Sheet 1'

		TK_FUNC_ARG_SEP,	// , or ; Function argument separator

		// Token errors
		TK_BAD_SYMBOL,
		TK_BAD_NUMBER,
		TK_BAD_SHEETREF,

		TK_MAX
	};

	void init (
		char	const	* const input,
		CedParser	* const	state
		)
	{
		m_input = m_token = m_symbol = input;
		m_cedparser = state;
	}

	int scan_token ();
		// TK_*

	std::string const & string () const	{ return m_string; }
	std::string const & string_lower ();	// Convert to lower case
	char const * quote_string () const	{ return m_quote_string; }
	double number () const			{ return m_number; }
	PCellRef const & pcellref () const	{ return m_pcellref; }
	CedSheet * sheetref () const		{ return m_pcellref.sheet; }

	int input_pos () const		{ return (int)(m_symbol - m_input); }

	void rewind_token ()			{ m_symbol = m_token; }

	char const * symbol_position () const	{ return m_symbol; }
	char const * token_position () const	{ return m_token; }
	void set_token_position ( char const * const pos )
						{ m_symbol = m_token = pos; }

	static int is_whitespace ( char ch )
	{
		switch ( ch )
		{
		case ' ':
		case '\t':
			return 1;
		}

		return 0;
	}

	static int is_alpha ( char ch )
	{
		if (	(ch >= 'a' && ch <= 'z')	||
			(ch >= 'A' && ch <= 'Z')
			)
		{
			return 1;
		}

		return 0;
	}

	static int is_numeric ( char ch )
	{
		if ( (ch >= '0' && ch <= '9') )
		{
			return 1;
		}

		return 0;
	}

private:
	int scan_number ();
	int scan_name_string ();	// variable/function name

	char scan_symbol ()	{ return *m_symbol++; }
	void rewind_symbol ()	{ m_symbol--; }

	void assign_string ()	// Tokenize the validated string
	{
		// Caller ensures (m_symbol >= m_token)
		m_string.assign ( m_token, (size_t)(m_symbol - m_token) );
	}

/// ----------------------------------------------------------------------------

	CedParser	* m_cedparser	= nullptr;

	char	const	* m_input	= nullptr;
	char	const	* m_token	= nullptr;
	char	const	* m_symbol	= nullptr;

	std::string	m_string;	// Most recent string extracted
	char const	* m_quote_string = nullptr;
	double		m_number	= 0.0;
	PCellRef	m_pcellref	= {nullptr, {0,0,0,0}};
};



}		// namespace mtCED



#endif	// CED_USE_BISON_PARSER



#endif		// __cplusplus



#ifndef DEBUG
#pragma GCC visibility pop
#endif



#endif		// CED_EVAL_H_

