package net.mc_pandacraft.java.util;

//******************************************************************************
//***
//***		INTERPRETEUR ARITHMETIQUE version 2.1			Version Java
//***
//***
//***
//******************************************************************************

/*
Historique:

2.1:
  - Version Java disponible:
  # les operateurs mathematiques disponibles sont ceux de Java donc certains manquent.

2.0: 
  - Portage en C++ et debut en Java

Version C++:
	 
  - Meilleure gestion memoire lors de la construction de l'expression.
  - Acceleration de certaines operations.

Version Java:
	 
  - Premiere version. Normalement ca doit marcher

1.3b: ajoute les fonctions suivantes: (NON DISTRIBUEE)
    - reconnaissance du symbole �
    - ecriture formatee d'expression (� ameliorer)

1.2: corrige les bugs suivants:
    - erreur sur l'interpretation de fonctions unaires imbriquees telles que ln(exp(x)).
    - la fonction puissance (pour les puissances entieres).
     ajoute:
    - la verification de la chaine entree (voir code d'erreur)
    - la verification de l'existence de l'evaluation (voir code d'erreur)
     

1.1: corrige un bug au niveau de l'interpretation des fonctions du type:

      exp(-(x+y))

1.0: premiere version

Le code source peut etre librement modifie et distribue.

Puisqu'il s'agit d'un  essai en Java, le code ne doit pas etre super genial et merite sans doute
quelques modifications.En particulier, il serait interessant de rajouter le support des Exceptions
pour les calculs (division par zero, etc...).
 
*/

// Classe servant � palier l'absence de passage par variables ou reference

class VariableInt {
	public int mValue;
}

// Classe principale

public class JArithmeticInterpreter {

	// Variables
	
		int mOperator;
		double mValue;
		JArithmeticInterpreter fg, fd;
	
	// Methods
	
	//.................................................................................... Node
	
	private JArithmeticInterpreter() {
		this(0, 0, null, null);
	}
	
	//.................................................................................... Node
	
	private JArithmeticInterpreter(int Operator,double Value,JArithmeticInterpreter Fg,JArithmeticInterpreter Fd) {
		mOperator=Operator;
		mValue=Value;
		fg=Fg;
		fd=Fd;
	}
	
	private JArithmeticInterpreter(int Operator,double Value) {
		this(Operator, Value, null, null);
	}
	
	//.................................................................................... Construct_Tree
	
	private static JArithmeticInterpreter  constructTree(StringBuffer string,int length,int  error) {
		int imbric,Bimbric;
		int priorite,ope;
		int position,positionv,i,j;
		int opetemp=0;
		int espa=0,espat=0;
		int caspp=0;
	
		JArithmeticInterpreter  node;
	
		// Initialisation des variables
	
		if (length<=0) {
			error=3;
			return null;
		}
	
		ope=0;
		imbric=0;Bimbric=128;
		priorite=6;
		i=0;
		positionv=position=0;
	
		// Mise en place des donnees sur le morceau de chaine
	
		while (i<length) {
			
			if (((string.charAt(i)>47) && (string.charAt(i)<58)) || (string.charAt(i)=='�')) {
				if (priorite>5) {
					priorite=5;
					positionv=i;
				}
				i++;
			}
			else {
			if ((string.charAt(i)>96) && (string.charAt(i)<117)) {
				VariableInt Vopetemp,Vespat;
				
				Vopetemp= new VariableInt();
				Vespat= new VariableInt();
				
				Vopetemp.mValue=opetemp;
				Vespat.mValue=espat;
				
				FindOperator(Vopetemp,Vespat,string,i);
				
				opetemp=Vopetemp.mValue;
				espat=Vespat.mValue;
				
				if (opetemp>=0) {
					if (imbric<Bimbric) {
						Bimbric=imbric;
						ope=opetemp;
						position=i;
						priorite=4;
						espa=espat;
					} 
					else if ((imbric==Bimbric) && (priorite >=4)) {
						ope=opetemp;
						position=i;
						priorite=4;
						espa=espat;
					}
					j=i+1;
					i+=espat;
					while(j<i) 
						j++;
					
				}
				else if (string.charAt(i)=='t') {
					if (priorite==6) ope=-1;
					i++;
				}
				else if (string.charAt(i)=='p') {
					if (priorite==6) ope=-2;
					i++;
				}
				else if (string.charAt(i)=='r') {
					if (priorite==6) ope=-2;
					i++;
				}
				else if (string.charAt(i)=='n') {
					if (priorite==6) ope=-1;
					i++;
				}
				else {
					error=2; // symbole non reconnu
		            return null;
		        }
			}
			else {
				switch(string.charAt(i)) {
					case '(':
						imbric++;
						i++;
						break;
					case ')':
						imbric--;
						i++;
						break;
					case '+':
						if (imbric<Bimbric) {
							Bimbric=imbric;
							priorite=1;
							ope=1;
							position=i;
							caspp=0;
						}
						else if ((imbric==Bimbric) && (priorite >=1)) {
							priorite=1;
							ope=1;
							position=i;
							caspp=0;
						}
						i++;
						break;
					case '-':
						if (imbric<Bimbric) {
							if ((i-1)<0) {
								if (priorite>5) {
									priorite=1;
									position=i;
									ope=2;
									Bimbric=imbric;
									caspp=1;
								}
							}
						 	else if (string.charAt(i-1)=='(') {
						 		if (priorite>1){
						 			priorite=1;
						 			position=i;
						 			Bimbric=imbric;
						 			caspp=1;
						 			ope=2;
						 		}
						 	}
							else {
								Bimbric=imbric;
								priorite=1;
								ope=2;
								position=i;
								caspp=0;
							}
						}
						else if ((imbric==Bimbric) && (priorite>=1)) {
							if ((i-1)<0) {
								if (priorite>5) {
									priorite=1;
									position=i;
									ope=2;
									caspp=1;
								}
							}
							else if (string.charAt(i-1)=='(') {
								if (priorite>1){
									priorite=1;
									position=i;
									caspp=1;
									ope=2;
								}
							}
							else {
								priorite=1;
								ope=2;
								position=i;
								caspp=0;
							}
						}
						i++;
						break;
					case '*':
						if (imbric<Bimbric) {
							Bimbric=imbric;
							priorite=2;
							ope=3;
							position=i;
						}
						else if ((imbric==Bimbric) && (priorite>=2)) {
							priorite=2;
							ope=3;
							position=i;
						}
						i++;
						break;
					case '/':
						if (imbric<Bimbric) {
							Bimbric=imbric;
							priorite=2;
							ope=4;
							position=i;
						}
						else if ((imbric==Bimbric) && (priorite>=2)) {
							priorite=2;
							ope=4;
							position=i;
						}
						i++;
						break;
					case '^':
						if (imbric<Bimbric) {
							Bimbric=imbric;
							priorite=3;
							ope=5;
							position=i;
						}
						else if ((imbric==Bimbric) && (priorite>=3)) {
							priorite=3;
							ope=5;
							position=i;
						}
						i++;
						break;
					case '.':
						i++;
						break;
					default:
						error=2; // symbole non reconnu
						return null;
					}
				}
			}
		}
	
		if (imbric!=0) {
			error=1; // erreur de "parenthesage"
			return null;
		}
	
		// Traitement des donnees	
	
		if (priorite==6) {
			node =new JArithmeticInterpreter(ope,0.0);
			return node;
		}
		else if (caspp==1) {
			node = new JArithmeticInterpreter(2,0);
			
			node.fg= new JArithmeticInterpreter(0,0);
			node.fd= new JArithmeticInterpreter();
						    
			if ((length-position-1-Bimbric)==0) { // argument absent
				error=3;
				return null;
				} 
			StringBuffer temp=CopyPartialString(string,(position+1),(length-1-Bimbric));
			node.fd=constructTree(temp,(length-position-1-Bimbric),error);
			
			return node;
			}
	
		else if (priorite==5) {
			node = new JArithmeticInterpreter(0,calc_const(string,positionv),null,null);
			
			return node;
		}
		else if (ope>5) {
			node = new JArithmeticInterpreter(ope,0,null,null);
			
			if ((length-position-espa-Bimbric)==0) { // argument absent
				error=3;
				return null;
			}
			StringBuffer temp=CopyPartialString(string,(position+espa),(length-1));
			node.fg=constructTree(temp,(length-position-espa-Bimbric),error);
			return node;
		} 
		else{
			node = new JArithmeticInterpreter(ope,0,null,null);
			
			if ((position-Bimbric)==0) { // argument absent
				error=3;
				return null;
			}
			StringBuffer temp=CopyPartialString(string,Bimbric,(position-1));
			node.fg=constructTree(temp,(position-Bimbric),error);
			if ((length-position-1-Bimbric)==0) { // argument absent
				error=3;
				return null;
			}
			temp=CopyPartialString(string,(position+1),(length-1-Bimbric));
			node.fd=constructTree(temp,(length-position-1-Bimbric),error);
			return node;
		}
	}
	
	//....................................................................................
	
	private double computeTree() {
		if (mOperator==0) return mValue;
		int error = 0;
	
		double valueL=fg.computeTree();
	
		if (error!=0) return 0;
		double valueR=0;
	
		if (fd!=null) valueR=fd.computeTree();
		if (error!=0) return 0;
	
		switch(mOperator) {
			case 1:								// +
				return (valueL+valueR);
			case 2:								// -
				return (valueL-valueR);
			case 3:								// *
				return (valueL*valueR);
			case 4:								// -
				if (valueR==0) {
					error=1;
					return 0;
				}
				return (valueL/valueR);
			case 5:								// ^
				return Math.pow(valueL,valueR);
			case 6:								// exp
				return Math.exp(valueL);
			case 7:								// ln
				if (valueL<=0) {
					if (valueL<0) error=2;
					else error=1;
					return 0;
				}
				return (Math.log(valueL)/Math.log(2));
			case 8:								// log
				if (valueL<=0) {
					if (valueL<0) error=2;
					else error=1;
					return 0;
				}
				return Math.log(valueL);
			case 9:								// sqrt
				if (valueL<0) {
					error=2;
					return 0;
				}
				return Math.sqrt(valueL);
			case 10:							// abs
				return Math.abs(valueL);
			case 11:
				return Math.sin(valueL);			// sin
			case 12:
				return Math.cos(valueL);			// cos
			case 13:
				return Math.tan(valueL);			// tan
			case 14:
				return Math.asin(valueL);			// asin
			case 15:
				return Math.acos(valueL);			// acos
			case 16:
				return Math.atan(valueL);			// atan
			default:
				return 0;
		}
	}
	
	//.................................................................................... Write_Tree
	
	private void writeTree(StringBuffer string) {
		boolean parenthese=false;
	
		switch(mOperator) {
			case 0:
				string.append(StringUtil.formatDouble(mValue));
				break;
			case 1:
				fg.writeTree(string);
				string.append('+');
				fd.writeTree(string);
				break;
			case 2:
				if ((fg.mOperator==0) && (fg.mValue==0));
				else fg.writeTree(string);
				string.append('-');
				if ((fd.mOperator==1) || (fd.mOperator==2)) {
					parenthese=true;
					string.append('(');
				}
				fd.writeTree(string);
				if (parenthese==true) {
					string.append(')');
				}
				break;
			case 3:
				if ((fg.mOperator==1) || (fg.mOperator==2)) {
					parenthese=true;
					string.append('(');
				}
				fg.writeTree(string);
				if (parenthese==true)
					string.append(')');
				parenthese=false;
				string.append('*');
				if ((fd.mOperator==1) || (fd.mOperator==2)) {
					parenthese=true;
					string.append('(');
				}
				fd.writeTree(string);
				if (parenthese==true)
					string.append(')');
				break;
			case 4:
				if ((fg.mOperator==1) || (fg.mOperator==2)) {
					parenthese=true;
					string.append('(');
				}
				fg.writeTree(string);
				if (parenthese==true)
					string.append(')');
				parenthese=false;
				string.append('/');
				if ((fd.mOperator>0) && (fd.mOperator<5)) {
					parenthese=true;
					string.append('(');
				}
				fd.writeTree(string);
				if (parenthese==true) 
					string.append(')');
				break;
			case 5:
				if ((fg.mOperator>0) && (fg.mOperator<5)) {
					parenthese=true;
					string.append('(');
				}
				fg.writeTree(string);
				if (parenthese==true)
					string.append(')');
				parenthese=false;
				string.append('^');
				if ((fd.mOperator>0) && (fd.mOperator<5)) {
					parenthese=true;
					string.append('(');
				}
				fd.writeTree(string);
				if (parenthese==true) 
					string.append(')');
				break;
			case 6:
				string.append("exp(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 7:
				string.append("log(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 8:
				string.append("ln(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 9:
				string.append("sqrt(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 10:
				string.append("|");
				fg.writeTree(string);
				string.append('|');
				break;
			case 11:
				string.append("sin(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 12:
				string.append("cos(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 13:
				string.append("tan(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 14:
				string.append("asin(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 15:
				string.append("acos(");
				fg.writeTree(string);
				string.append(')');
				break;
			case 16:
				string.append("atan(");
				fg.writeTree(string);
				string.append(')');
				break;
		}
	}
	
	//.................................................................................... calc_const
	
	private static double calc_const(StringBuffer chaine,int pos) {
		int i=pos,j;
		double temp=0;
		int signe=1;
		int longueur=chaine.length();
	
		
		if (chaine.charAt(i)=='-') {
			signe=-1;
			i++;
		}
		if (chaine.charAt(i)=='π') return signe*Math.PI;
		
		while (i<longueur && chaine.charAt(i)>47 && chaine.charAt(i)<58) {
			temp=temp*10+(chaine.charAt(i)-48);
			i++;
		}
		if (i<longueur && chaine.charAt(i)=='.') {
			i++;
			j=1;
			while (i<longueur && chaine.charAt(i)>47 && chaine.charAt(i)<58) {
				temp=temp+(chaine.charAt(i)-48)*Math.exp(-j*2.30258509);
				i++;
				j++;
			}
		}
		return (signe*temp);
	}
	
	//.................................................................................... FindOperator
	
	private static void FindOperator(VariableInt oper,VariableInt esp,StringBuffer chaine,int pos) {
		switch(chaine.charAt(pos)) {
		case 'a':
			switch(chaine.charAt(pos+1)) {
				case 'b':
					esp.mValue=3;
					oper.mValue=10;
					break;
				case 'c':
					esp.mValue=4;
					oper.mValue=15;
					break;
				case 's':
					esp.mValue=4;
					oper.mValue=14;
					break;
				case 't':
					esp.mValue=4;
					oper.mValue=16;
					break;
			}
			break;
		case 'c':
			if (chaine.charAt(pos+1)=='h') {
				esp.mValue=2;
				oper.mValue=18;
			}
			else if ((chaine.charAt(pos+1)=='o') && (chaine.charAt(pos+2)=='s')) {
				if (chaine.charAt(pos+3)=='h') {
					esp.mValue=4;
					oper.mValue=18;
				}
				else {
	         		esp.mValue=3;
					oper.mValue=12;
				}
			}
			break;
		case 'e':
			if ((chaine.charAt(pos+1)=='x') && (chaine.charAt(pos+2)=='p')) {
				esp.mValue=3;
	         	oper.mValue=6;
			}
			else oper.mValue=-10;
			break;
		case 'l':
			if (chaine.charAt(pos+1)=='n') {
				esp.mValue=2;
				oper.mValue=7;
			}
			else if ((chaine.charAt(pos+1)=='o') && (chaine.charAt(pos+2)=='g')){
	         	esp.mValue=3;
	            oper.mValue=8;
	         }
	         else oper.mValue=-10;
	         break;
		case 's':
			if (chaine.charAt(pos+1)=='h') {
				esp.mValue=2;
				oper.mValue=17;
			}
			else if (chaine.charAt(pos+1)=='q') {
				esp.mValue=4;
				oper.mValue=9;
			}
			else {
				if (chaine.charAt(pos+3)=='h') {
					esp.mValue=4;
					oper.mValue=17;
				}
				else {
					esp.mValue=3;
					oper.mValue=11;
				}
			}
			break;
		case 't':
			if (chaine.charAt(pos+1)=='h') {
				esp.mValue=2;
				oper.mValue=19;
			}
			else if ((chaine.charAt(pos+1)=='a') && (chaine.charAt(pos+2)=='n')) {
				if (chaine.charAt(pos+3)=='h') {
					esp.mValue=4;
					oper.mValue=19;
				}
				else {
					esp.mValue=3;
					oper.mValue=13;
				}
			}
			else oper.mValue=-10;
			break;
		default:
			oper.mValue=-10;
			break;
		}
	}
	
	//.................................................................................... CopyPartialString
	
	private static StringBuffer CopyPartialString(StringBuffer chaine,int debut,int fin) {
		StringBuffer  chartemp;
		int a=fin-debut+1;
		chartemp=new StringBuffer(a+1);
	
		for(int i=0;i<a;i++) chartemp.append(chaine.charAt(debut+i));
	
		return chartemp;
	}
	
	
	public static double getResultFromExpression(String expr, StringBuffer writeTree)
	{
		StringBuffer input = new StringBuffer(expr);

		JArithmeticInterpreter jai = null;
		
		try {
			jai = JArithmeticInterpreter.constructTree(input,input.length(),0);
		} catch (Exception e) {  }
		
		if (jai==null)
			throw new IllegalArgumentException("Le calcul passé en paramètre est invalide");
		
		if (writeTree != null)
		{
			writeTree.setLength(0);
			jai.writeTree(writeTree);
		}
		
		return jai.computeTree();
	}
	
	public static double getResultFromExpression(String expr)
	{
		return getResultFromExpression(expr, null);
	}
	
	
	public static void main(String args[]) {
		
		StringBuffer b = new StringBuffer(0);
		
		String disp_res = StringUtil.formatDouble(JArithmeticInterpreter.getResultFromExpression("1245.25*2", b));
		
		System.out.println(disp_res);
		System.out.println(b);
	} // */
	
}
