package gve.calc.formula;

import java.awt.*;

public class OperatorSet extends InfixBinaryOp {
	private byte kind;

	public static final byte IN = 0;
	public static final byte SUBSETEQ = 1;
	// Many more to come ...

	public String getName() {
		switch (kind) {
		case IN: return "in";
		case SUBSETEQ: return "subseteq";
		default: return null;
		}
	}

	public int getPri() { return 1400; }

	public OperatorSet(String name,Part l,Part r) {
		super(l,r);
		switch(name.charAt(0)) {
		case 'i': kind = IN; break;
		case 's': kind = SUBSETEQ; break;
		default: throw new Error("Bad set operator name "+name);
		}
	}

	public OperatorSet(byte kind,Part l,Part r) {
		super(l,r);
		this.kind = kind;
	}

	public byte getKind() { return kind; }

	public void write(java.io.Writer w) throws java.io.IOException {
		super.write(w);
		w.write(""+(int)kind);
		w.write('\n');
	}

	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);
		int kind = Integer.parseInt(r.readLine());
		 return new OperatorSet((byte)kind,left,right);
	}

	public static boolean in(Part p,Part list) {
		CommaEnumerator enum = new CommaEnumerator(list);
		while (enum.hasMoreElements()) {
			if (p.same(enum.nextPart())) return true;
		}
		return false;
	}

	public static boolean subseteq(Part small,Part big) {
		CommaEnumerator enum = new CommaEnumerator(small);
		while (enum.hasMoreElements()) {
			if (!in(enum.nextPart(),big)) return false;
		}
		return true;
	}

	public Part evaluate(Evaluator ev) {
		Part l = left.evaluate(ev), r = right.evaluate(ev);
		switch (kind) {
		case IN:
			if (r instanceof Brackets) {
				Brackets set = (Brackets)r;
				if (set.getKind() != Brackets.CURLY)
					return new Boolean(false);
				// Now we know that 'set' is of the form { ... }
				return new Boolean(in(l,set.child));
			} else return new Boolean(false);
		case SUBSETEQ:
			if (r instanceof Brackets && l instanceof Brackets) {
				Brackets rset = (Brackets)r;
				if (rset.getKind() != Brackets.CURLY)
					return new Boolean(false);
				Brackets lset = (Brackets)l;
				if (lset.getKind() != Brackets.CURLY)
					return new Boolean(false);
				return new Boolean(subseteq(lset.child,rset.child));
			} else return new Boolean(false);
		default:
			return null;
		}
	}

	public boolean same(Object o) {
		o = Brackets.unbracket(o);
		if (!(o instanceof OperatorSet)) return false;
		OperatorSet obj = (OperatorSet)o;
		return kind==obj.kind && left.same(obj.left) && right.same(obj.right);
	}

	public Component createView(FormulaView view) {
		return new InfixBinaryOpView(this,view,new OperatorSetSymbolView(kind,getName()));
	}

	public void saveOperatorLatex(java.io.BufferedWriter w) throws java.io.IOException {
		switch (kind) {
		case IN: w.write("\\in"); break;
		case SUBSETEQ: w.write("\\subseteq"); break;
		}
	}
}

class OperatorSetSymbolView extends TextSymbolView {
	private byte kind;
	public OperatorSetSymbolView(byte kind,String str) {
		super(str);
		this.kind = kind;
	}

	public Dimension getSymbolSize() {
		FontMetrics metrics = getFontMetrics(getFont());
		return new Dimension(metrics.charWidth(' ')*2,metrics.getHeight());
	}

	public void paintSymbol(Graphics g) {
		int spatWidth = g.getFontMetrics().charWidth(' ');
		int mBase = g.getFontMetrics().getAscent();
		int opWidth = spatWidth*2;
		switch (kind) {
		case OperatorSet.IN: paintIn(g,0,mBase/3,opWidth,(mBase*2+2)/3); break;
		case OperatorSet.SUBSETEQ: paintSubseteq(g,0,mBase/3,opWidth,(mBase*2+2)/3); break;
		}
	}

	private void paintIn(Graphics g,int x,int y,int w,int h) {
		g.drawArc(x,y,w+1,h,90,180);
		g.drawLine(x+w/2,y,x+w-1,y);
		g.drawLine(x+w/2,y+h,x+w-1,y+h);
		// middelste lijntje
		g.drawLine(x,y+h/2,x+w-1,y+h/2);
	}

	private void paintSubseteq(Graphics g,int x,int y,int w,int h) {
		g.drawArc(x,y,w,h-2,90,181);
		g.drawLine(x+w/2,y,x+w-1,y);
		g.drawLine(x+w/2,y+h-2,x+w-1,y+h-2);
		// onderste lijntje
		g.drawLine(x,y+h,x+w-1,y+h);
	}
}