#pragma once

#include "Variant.h"

using namespace System;
using namespace System::IO;
using namespace System::Text;
using namespace System::Collections::Generic;
using namespace System::Text::RegularExpressions;

namespace CLITableur
{    
	/// <summary>
	/// Evaluateur de formule
	///	</summary>
public
	ref class Evaluator
	{
		/*
		 * Analyseur grammatical
		 */
	public: 
		/// <summary>
		/// fonction obtenant la valeur numérique d'une cellule nommée
		///	</summary>
		delegate void del_get_cell_value(String^ name, double & value); 	

		/// <summary>
		/// Evénement déclenché pour obtenir la valeur numérique d'une cellule nommée
		///	</summary>
		event del_get_cell_value^ OnGetCellValue; 

	/*
	* Grammeire LL(1) 
	* Parseur d'analyse descendante

	S:			
		expression				

	expression:   
		terme
		terme + terme			
		terme - terme


	terme:		
		facteur
		facteur * facteur		
		facteur / facteur																


	facteur:		
		nombre						
		cellule		
		( expression )	
		sqrt ( expression )

		abs ( expression )
		arr ( expression )

		ln (expression)
		log10 ( expression )
		exp ( expression )
		pow ( nb:expression ; p:expression ) 
		fac ( expression )

		cos ( expression )
		sin ( expression )
		tan ( expression )
		cosh ( expression )
		sinh ( expression )
		tanh ( expression )
		acos ( expression )
		asin ( expression ) 
		atan ( expression )
		deg ( expression )
		rad ( expression ) 

		pmnt ( loan:expression ; rate:expression ; duration: expression)
		amort ( loan:expression ; rate:expression ; duration: expression ; period:expression)
		inter loan:expression ; rate:expression ; duration: expression) ; period:expression)

		" texte "
		if ( predicat ; expression_true ; expression_false )
		sum ( range )
		avg ( range )
		max ( range )
		min ( range )
		var ( range )						

	cellule: 				
		[a-z|A-Z]+\d+ 

		$[a-z|A-Z]+\d+ 
		L(-?\d+)C(-?\d+)
		@[a-z|A-Z][a-z|A-Z|0-9]*

	range:		
		cellule : cellule	

	predicat:		
		expression = expression
		expression <> expression
		expression < expression
		expression > expression
		expression <= expression
		expression >= expression
	*/


			
	protected: 
		/// <summary>
		/// Axiome S
		///	</summary>
		Variant^ S();	

		/// <summary>
		/// production expression
		///	</summary>
		void expression(Variant^ valeur);		

		/// <summary>
		/// production terme
		///	</summary>
		void terme(Variant^ t);		

		/// <summary>
		/// production facteur
		///	</summary>
		void facteur(Variant^ f);  

		/// <summary>
		/// production predicat
		///	</summary>
		void predicat(Variant^ p);
		
		/// <summary>
		/// production cellule
		///	</summary>
		void cellule(String^ name, Variant^ f);

		/// <summary>
		/// production range
		///	</summary>
		void range(String^ depart,String^ arrivee,array<double,2> ^ vals);

		/// <summary>
		/// production sum
		///	</summary>
		void sum(String^ depart, String^ arrive, Variant^ c);

		/// <summary>
		/// production avg
		///	</summary>
		void avg(String^ depart, String^ arrive, Variant^ c);

		/// <summary>
		/// production min
		///	</summary>
		void min(String^ depart, String^ arrive, Variant^ c);

		/// <summary>
		/// production max
		///	</summary>
		void max(String^ depart, String^ arrive, Variant^ c);

		/// <summary>
		/// production var
		///	</summary>
		void var(String^ depart, String^ arrive, Variant^ c);
	
		/// <summary>
		/// journalise un événement (message paramétré {0}...)
		///	</summary>
		void log(String^ message, ... array<System::Object^> ^ p );

	public:
		/// <summary>
		/// Convertit un nom de cellule A1 en index de ligne et colonne
		///	</summary>
		void CellNameToColRow(String^ depart, int & col, int & row);		

		/// <summary>
		/// Lance l'analyse de la formule et calcule la valeur correspondante
		///	</summary>
		String^ parse();

	private:
		/// <summary>
		/// Journal d'erreurs
		///	</summary>
		StringBuilder^ errors;

	public:
		/// <summary>
		/// Obtient les erreurs
		///	</summary>
		StringBuilder^ getErrors();

		/// <summary>
		/// Définit les erreurs
		///	</summary>
		void setErreors(StringBuilder^ value);

	private:
		/// <summary>
		/// L'évaluation a provoqué une erreur (membre privé)
		///	</summary>
		bool hasErrors;

	public:
		/// <summary>
		/// Indique si l'évaluation a provoqué une erreur (membre privé)
		///	</summary>
		property bool HasErrors 
		{
			bool get() 
			{
				return hasErrors; 
			}
		}
			
		/// <summary>
		/// Ajoute une erreur
		///	</summary>
		void addError(String^ message);      

	public:
		/// <summary>
		/// Constructeur
		///	</summary>
		Evaluator();

	private:
		/// <summary>
		/// fonctionnement en débug (membre privé)
		///	</summary>
		bool debug;

	public:
		/// <summary>
		/// Indique si le fonctionnement est en débug
		///	</summary>
		bool IsDebug() 
		{
			return debug; 
		}

		/// <summary>
		/// Active le fonctionnement en debug
		///	</summary>
		void setDebug(bool value)
		{
			debug = value; 
		}

	private:
		/// <summary>
		/// Texte à analyser (membre privé)
		///	</summary>
		String^ text;

	public:
		/// <summary>
		/// Obtient le texte à analyser
		///	</summary>
		String^ getText();

		/// <summary>
		/// Définit le texte à analyser
		///	</summary>
		void setText(String^ value);

	private:
		/// <summary>
		/// flux de lecture (membre privé)
		///	</summary>
		StringReader^ reader;
	public:
		/// <summary>
		/// Obtient le flux de lecture
		///	</summary>
		StringReader^ getReader();

		/// <summary>
		/// Définit le flux de lecture
		///	</summary>
		void setReader(StringReader^ value);



		/*
		 * Analyseur lexical
		 */
	public:
		/// <summary>
		/// Symboles du lexique (tokens)
		///	</summary>
		enum class tokens
		{
			POUV, PFERM, PLUS, MOINS, MUL, DIV, TEXT, EOF,
			SUM, DEUXPOINT, AVG, MIN, MAX, VAR, SQRT, IF,
			EQ, NEQ, LT, GT, LEQ, GEQ, SEMI, QUOTE,

			ABS, ARR, LN, LOG10, EXP, POW, FAC,		
			COS, SIN, TAN, SINH, COSH, TANH,
			ACOS, ASIN, ATAN, DEG, RAD, PI,

			PMNT, AMOR, INTER
		};

	public:
		/// <summary>
		/// Valeur courante de l'analyseur
		///	</summary>
		String^ current_value;	

		/// <summary>
		/// symbole courant de l'analyseur
		///	</summary>
		tokens current_token;	

		/// <summary>
		/// prochaine valeur de l'analyseur
		///	</summary>
		String^ next_value;	

		/// <summary>
		/// prochain symbole de l'analyseur
		///	</summary>
		tokens next_token;
	
		/// <summary>
		/// Reste-t-il encore quelque chose à analyser ?
		///	</summary>
		bool has_next; // = false;
		
	public:
		/// <summary>
		/// Renvient en arrière
		///	</summary>
		void back();			

		/// <summary>
		/// Scrute le prochain symbole
		///	</summary>
		tokens look_ahead();		

		/// <summary>
		/// Avance d'un symbole
		///	</summary>
		tokens next();


	public: 
		/// <summary>
		/// Identifie toutes les références de cellules dans une formule
		///	</summary>
		static array<String^>^ GetNames(String^ formula);	

		/// <summary>
		/// Détermine la référence de cellule A1 à partir de l'index de colonne et de ligne
		///	</summary>
		static String^ ColRowToName(int col, int row);  
	};
}

