package gve.calc.formula;
public class OperatorSpace extends InfixBinaryOp {
	public String getName() { return " "; }
	/* The priority of the space operator is, as far as we seem to see, irrelevant.
	 * We kiezen 10000 om "x^f(y)" te associeren als "x^(f(y))". */
	public int getPri() { return 10000; }
	public int getLeftPri() { return 10010; }
	public int getRightPri() { return 9990; }
	public OperatorSpace(Part l,Part r) {
		super(l,r);
	}
	public Part evaluate(Evaluator ev) {
		if (right == null) return null;
		if (left == null) return null;
		if (left instanceof Identifier) {
			String fname = ((Identifier)left).getString();
			// Note: we don't ev.call(fname,right.evaluate(ev)) since some functions work
			// on expressions, e.g. freevar(x=y) may not be evaluated as
			// freevar(false) but must be evaluated as freevar(x=y) ...
			Part result = ev.call(fname,right);
			if (result == null)
				return new OperatorSpace(new Identifier(fname),right.evaluate(ev));
			return result;
		}
		Part arg = right.evaluate(ev);	// argument
		return new OperatorSpace(left.evaluate(ev),arg);
	}
	public boolean recognizeOp(Formula f) {
		return recognizeOp(f,null);
	}
	public boolean recognizeOp(FormulaView view) {
		return recognizeOp(view.getFormula(),view);
	}
	|  Call this method on a freshly inserted space operator.
	  This method checks if with this fresh space, we can recognize an operator.
	  If so, this method returns true; in that case, the space (and some
	  Identifier---the recognized operator) is no longer part of the formula.
	  If the view argument is non-null, take special care that the cursor is in a
	  "nice" position after rotating if necessary.  | 
	public boolean recognizeOp(Formula f,FormulaView view) {
		rotate(f,view);	// rotate myself into the right position, between possible other spaces
		if (recognizeOpWithParent(f,view)) return true;
		if (left instanceof OperatorSpace) {
			if (((OperatorSpace)left).recognizeOpWithParent(f,view)) return true;
//			return false;
		}
		if (right instanceof OperatorSpace) {
			if (((OperatorSpace)right).recognizeOpWithParent(f,view)) return true;
//			return false;
		}
		if (left instanceof Identifier) {
			Identifier ident = (Identifier)left;
			if (PrefixUnaryOp.isOperatorName(ident.getString())) {
				// Recognize prefix unary op
// using this approach, right.parent ends up bad
//				UnaryOp op = PrefixUnaryOp.getInstance(ident.getString(),right);
//				f.replace(this,op);
				Identifier dummy = new Identifier("");
				UnaryOp op = PrefixUnaryOp.getInstance(ident.getString(),dummy);
				f.replace(this,op);
				f.replace(dummy,right);
				if (view != null) {
					Part cp = view.getCursorPart();
					if (cp == this)
						view.setCursorPart(op,HasCursorPos.Somewhere_MYRIGHT);
					else if (cp==left) {
						int pos = ((HasCursorPos)view.getView(cp)).getCursorPos();
						view.setCursorPart(op,pos);
					}
				}
				op.rotate(f,view);
				return true;
			}
		} else if (right instanceof Identifier) {
			Identifier ident = (Identifier)right;
			if (PostfixUnaryOp.isOperatorName(ident.getString())) {
				// Recognize postfix unary op
				UnaryOp op = PostfixUnaryOp.getInstance(ident.getString(),left);
				f.replace(this,op);
				op.rotate(f,view);
				return true;
			}
		}
		return false;
	}
	|  If the parent is also a space operator, try to recognize a new operator.  | 
	private boolean recognizeOpWithParent(Formula f,FormulaView view) {
		if (!(parent instanceof OperatorSpace)) return false;
		OperatorSpace par = (OperatorSpace)parent;
		if (par.right==this && left instanceof Identifier) {
			Identifier leftid = (Identifier)left;
			if (InfixBinaryOp.isOperatorName(leftid.getString())) {
				InfixBinaryOp rot = InfixBinaryOp.getInstance(leftid.getString(),par.left,right);
				f.replace(par,rot);
				if (view != null) {
					Part cp = view.getCursorPart();
					if (cp == this)
						view.setCursorPart(rot,HasCursorPos.Somewhere_MYRIGHT);
					else if (cp==left) {
						int pos = ((HasCursorPos)view.getView(cp)).getCursorPos();
						view.setCursorPart(rot,pos);
					}
				}
				rot.rotate(f,view);
				return true;
			} else if (leftid.getString().equals("")) {
				// An empty identifier between two other Part trees => gobble up
				if (par.left instanceof Identifier && right instanceof Identifier) {
					// Special case: join two Identifiers
					// NOTE (& PENDING:) that this is very ad hoc: it works only if the two
					// Identifiers are directly next to each other
					// e.g. if you type "a*b+[]c*d" ([] == cursor) and press backspace,
					// the trick doesn't quite work ...
					Identifier a = (Identifier)par.left;
					Identifier b = (Identifier)right;
					Identifier newident = new Identifier(a.getString() + b.getString());
					f.replace(par,newident);
					if (view != null)
						view.setCursorPart(newident,a.getString().length());
				}
				f.replace(this,right);
				if (view != null) {
					Part cp = view.getCursorPart();
					if (cp==this || cp==left)	// cursor is on parts that vanished
						view.setCursorPart(right);
				}
			}
		} else if (par.left==this && right instanceof Identifier) {
			Identifier rightid = (Identifier)right;
			if (InfixBinaryOp.isOperatorName(rightid.getString())) {
				InfixBinaryOp rot = InfixBinaryOp.getInstance(rightid.getString(),left,par.right);
				f.replace(par,rot);
				// PENDING: reposition cursor! [6-dec-2000]
				// (see above case)
				rot.rotate(f,view);
				return true;
			}
		}
		return false;
	}
	public static Part read(java.io.BufferedReader r) throws java.io.IOException,
			ClassNotFoundException,NoSuchMethodException,
			java.lang.reflect.InvocationTargetException,IllegalAccessException{
		Part left = Part.read(r);
		Part right = Part.read(r);
		return new OperatorSpace(left,right);
	}
	public boolean same(Object o) {
		if (o instanceof OperatorSpace) {
			OperatorSpace spat = (OperatorSpace)o;
			return spat.left.same(left) && spat.right.same(right);
		}
		return false;
	}
	public void saveOperatorLatex(java.io.BufferedWriter w) throws java.io.IOException {
		if (right instanceof Brackets) ;
		else w.write("\\ ");
	}
}