package	hussam.math.operations.parser;

import hussam.math.operations.*;
import hussam.math.operations.dataBase.*;
import hussam.math.operations.extra.*;
import hussam.math.operations.factory.*;
import hussam.math.operations.parser.exceptions.*;
import java.util.*;
import java.util.regex.*;

public class HaniExpressionReader extends ExpressionReader{
	Iterator <String> scanner;
	String current;
	List<OperatorFactory<Operand>>sortedFactory;
	OperationsParser parser;
	OperatorSourceWrapper operatorSource;
	boolean createVariablesAsNeeded=true;
	boolean autoOptimize=true;
	List<Operation>operations;//not used
	List<String>parts;//not used currently

	public HaniExpressionReader(OperationsParser parser,OperatorSource source, boolean autoCreateVariables, boolean autoOptimize){
		operations=new Vector<Operation>();
		parts=new Vector<String>();
		this.parser= (parser==null)	? new MyParser():parser;
		if(source==null)source=OperatorFactoryDB.getInstance();
		operatorSource=new OperatorSourceWrapper(source);
		createVariablesAsNeeded=autoCreateVariables;
		this.autoOptimize=autoOptimize;
		sortedFactory=new Vector<OperatorFactory<Operand>>(OperatorFactoryDB.getInstance().getOperandFactories().values());
		Collections.sort(sortedFactory);
	}
	public HaniExpressionReader(){
		this(null,null, true, true);
	}
	private void moveCurrentNode(){
		if(scanner.hasNext())current=scanner.next();
		if(current.length()==0&scanner.hasNext())moveCurrentNode();
	}
	public OperationGroup readOperation(String stringOperation)throws MathException{
		List<String>strings=readSubStrings(stringOperation, getSubStringPositions(stringOperation));
//		Collections.reverse(strings);
		scanner=strings.iterator();
		current=scanner.next();
		OperationGroup o1=parseGroup();
		if(autoOptimize)o1=o1.optimize();
		return o1;
	}	
	public OperatorSource getOperatorSource(){return operatorSource;}
	OperationGroup parseGroup()throws MathException{
		MyGroup g=new MyGroup();
		boolean flag=parser.isOpenParenthesis(current);
		String p=current;
		if(flag)this.moveCurrentNode();
		do{
			if(parser.isParenthesis(p,current))break;
			Operation o1=parseOperands();
			if(o1!=null)
				g.addOperation(o1);
		}while(scanner.hasNext());
		return g;
	}
	Operation parseOperands()throws MathException{
		return parseOperand(sortedFactory, 1,0);
	}
	private int getNextPowerLevel(List<OperatorFactory<Operand>>ops,int position){
		int power=ops.get(position).getType();
		while((position<ops.size())&&(power==ops.get(position).getType()))position++;
		return position;
	}
	Operation parseOperand(List<OperatorFactory<Operand>>ops,int position, int currentPower)throws MathException{
		Operation left=null;
		if(position<ops.size()){
			left=parseOperand(ops, getNextPowerLevel(ops,position),currentPower);//getNextPowerLevel(ops,position));
		}else return readTerm();
		if(current.length()!=0)
		if(parser.isComma(current)){
			moveCurrentNode();
		}else
		if(parser.isOperand(current)){//.equals(ops.get(position).getName())){
			String c=current;
			int power=OperatorFactoryDB.getInstance().getOperandFactory(c).getType();
			
			if(power<=currentPower){//||(power==ops.get(position+1).getType())){
				return left;	
			}
			Operand o1=OperatorFactoryDB.getInstance().getOperandFactory(c).
					getNewOperator();
			moveCurrentNode();
			Operation right=parseOperand(ops,0,power);
			o1.set(left,right);
			return o1;
		}
		return left;
	}
	Operation getVarOrFunction()throws MathException{
		String name=current;
		moveCurrentNode();
		if(parser.isOpenParenthesis(current)){
			Function f=operatorSource.getFunctionFactory(name).getNewOperator();
			f.setOperationGroup((OperationGroup)readTerm());
			return f;
		}else{
			Variable v=operatorSource.getGlobalVariable(name);
			if(v==null)v=operatorSource.getLocalVariable(name);
			if(v==null&&createVariablesAsNeeded){
				operatorSource.addLocalVariable(new Variable(name));
				v=operatorSource.getLocalVariable(name);
			}
			if(v==null)throw new UnknownExpressionArgumentException(name, 0);
			
			return v;
		}
	}
	Operation readTerm()throws MathException{
		Operation term=null;
		if(parser.isOpenParenthesis(current)){
	//		if(scanner.hasNext())current=scanner.next();
			term= parseGroup();
		}else if(parser.isOperand(current)){
			Operand o=operatorSource.getOperandFactory(current).getNewOperator();
			moveCurrentNode();
			o.setNext(readTerm());
			return o;
		}
		else if(parser.isVariable(current)){
			term=getVarOrFunction();
			return term;
		}
		else if(parser.isNumber(current)){
			term= new ConstantNumber(Double.parseDouble(current));
		}
		moveCurrentNode();
		return term;
	}
	private	List<Integer>getSubStringPositions(String operation){
		Vector<Integer>parts=new Vector<Integer>();
		char NULL='\u0000';
		char previous=NULL;
		for(int	i=0;i<operation.length();i++){
			char current=operation.charAt(i);
			if(current==' ')continue;
			if(parser.isNumber(current)){
				if(parser.isOperand(previous)||parser.isComma(previous)||
						parser.isOpenParenthesis(previous)||parser.isCloseParenthesis(previous)||
						previous==NULL)
					parts.add(i);
			}else
				if(parser.isOperand(current)||parser.isComma(current)||
					parser.isOpenParenthesis(current)||parser.isCloseParenthesis(current)||
					previous==NULL){
					if((previous=='<' && current ==	'=') ||
							(previous=='<' && current =='>') ||
							(previous=='>' && current =='=')||
							(previous=='=' && current=='='));
					else{
						parts.add(i);
					}
				}else{
				if(parser.isOperand(previous)||parser.isComma(previous)||
						parser.isOpenParenthesis(previous)||parser.isCloseParenthesis(current)||
						previous==NULL)
					parts.add(i);
				}
			previous=current;
		}
		parts.add(operation.length());
		return parts;
	}
	private	static List<String>	readSubStrings(
			String operation, List<Integer>subs){
		Vector<String>strings=new Vector<String>();
		for(int	i=0;i<subs.size()-1;i++){
			strings.add(operation.substring(subs.get(i), subs.get(i+1)).trim());
		}
		return strings;
	}
	public static void main(String[]args)throws Exception{
	//	String exp="+5*+4";
		String exp="max(8/8/8/8,8-8-8)";
		System.out.println (exp);
		HaniExpressionReader reader=new HaniExpressionReader(null,new DynamicOperatorSourceWrapper(OperatorFactoryDB.getInstance()),true, false);
		Operation o1= reader.readOperation(exp);
		System.out.println (o1);
		System.out.println (o1.result());
	}
	
}
//*/

