package sdsi;
import sdsi.sexp.*;

/**
 * This tag represents the set of byte-strings within some bounded range.
 *
 * @see RangeComparator
 * @see RCAlpha
 * @see RCAny
 * @todo implement other forms of ranges defined by SPKI as RangeComparator
 * subclasses
 */
class TERange
	extends TESpecial {

	String rangeType;
	RangeComparator lowerComparator;
	RangeComparator upperComparator;

	String mySpecialToken() { return "range"; }

	public TERange(String rangeType,
		RangeComparator lower, RangeComparator upper) {
		this.rangeType = rangeType;
		this.lowerComparator = lower;
		this.upperComparator = upper;
		int count = 3;
		if (!(lowerComparator instanceof RCAny)) {
			count+=2;
		}
		if (!(upperComparator instanceof RCAny)) {
			count+=2;
		}
		Sexp[] sexpary = new Sexp[count];
		sexpary[0] = new SexpString("*");
		sexpary[1] = new SexpString(mySpecialToken());
		sexpary[2] = new SexpString(rangeType);
		int next=3;
		if (!(lowerComparator instanceof RCAny)) {
			sexpary[next++] = lowerComparator.getOperator();
			sexpary[next++] = lowerComparator.getLimit();
		}
		if (!(upperComparator instanceof RCAny)) {
			sexpary[next++] = upperComparator.getOperator();
			sexpary[next++] = upperComparator.getLimit();
		}
		srep = new SexpList(sexpary);
	}

	public TERange(SexpList l)
		throws SexpParseException {
		super(l);

		int argc = countArgs();

		if (!(argc==1 || argc==3 || argc==5)) {
			throw new SexpParseException(
				"*-range type tag not encoded properly");
		}

		rangeType = ((SexpString)l.elementAt(2)).stringContent();

		for (int i=2; i<argc+2; i++) {
			if (!(l.elementAt(i) instanceof SexpString)) {
				throw new SexpParseException(
					"*-range type tag not encoded properly");
			}
		}

		lowerComparator = RCAny.getInstance();
		upperComparator = RCAny.getInstance();

		// look at both range indicators and set comparators accordingly
		for (int i=3; i<argc+2; i+=2) {
			SexpString operatorSexp = ((SexpString)l.elementAt(i));
			String operator = operatorSexp.stringContent();
			SexpString valueSexp = ((SexpString)l.elementAt(i+1));
			String value = valueSexp.stringContent();

			if (operator.charAt(0)=='g'
				&& upperComparator instanceof RCAny) {
				upperComparator =
					getComparator(rangeType, operatorSexp, valueSexp);
			} else if (operator.charAt(0)=='l'
				&& lowerComparator instanceof RCAny) {
				lowerComparator =
					getComparator(rangeType, operatorSexp, valueSexp);
			} else {
				throw new SexpParseException(
					"*-range type tag not encoded properly");
			}
		}
	}

	public TagExpression intersect(TagExpression otherTE) {
		if (otherTE instanceof TEByteString) {
			// test for inclusion
			TEByteString tebs = (TEByteString) otherTE;
			if (lowerComparator.includes((SexpString) tebs.getSexp())
				&& upperComparator.includes((SexpString) tebs.getSexp())) {
				return tebs;
			} else {
				return TENull.getNull();
			}
		} else if (otherTE instanceof TERange) {
			TERange ter = (TERange) otherTE;
			// intersect the ranges
			RangeComparator upper =
				upperComparator.intersect(ter.upperComparator);
			RangeComparator lower =
				lowerComparator.intersect(ter.lowerComparator);
			return new TERange(rangeType, upper, lower);
		} else if (otherTE instanceof TESet) {
			// intersect with all members of the set --
			// TESet knows how to do that
			return otherTE.intersect(this);
		} else if (otherTE instanceof TEPrefix) {
			// I don't know how to intersect a range of bytestrings with
			// a prefix of bytestrings and produce a single meaningful output
			// (without a conjunction operator, of course :v)
			System.err.println("Unimplemented: Range *intersect* Prefix.");
			return TENull.getNull();
		} else {
			// range doesn't intersect with nonbytestring thing
			return TENull.getNull();
		}
	}

	public RangeComparator getComparator(String rangeType,
		SexpString operator, SexpString value)
		throws SexpParseException {

		if (rangeType.equals("alpha")) {
			// I'm very skeptical that this comparator is rigorously defined.
			// (I sure haven't seen the definition.)
			return new RCAlpha(operator, value);
		} else if (rangeType.equals("numeric")) {
			// return new RCNumeric(operator, value);
		} else if (rangeType.equals("time")) {
			// return new RCTime(operator, value);
		} else if (rangeType.equals("binary")) {
			// lexically-binary octet comparison?
			// (so abcd < abce < abcef < abcg)
			// return new RCBinary(operator, value);
		} else if (rangeType.equals("date")) {
			// return new RCDate(operator, value);
		} else {
			throw new SexpParseException("Unknown range ordering: "+rangeType);
		}
		throw new SexpParseException("Unimplemented range ordering: "
			+rangeType);
	}
}
