package sdsi;

import sdsi.sexp.*;

/**
 * The SPKI Threshold Subject -- it speaks for the issuer when k of the
 * n listed principals agree on the statement; that is, when one can
 * prove that one speaks for k different principals of the n listed.
 *
 * @author jonh@cs.dartmouth.edu
 */
public class ThresholdSubject
	extends SDSIObject implements Subject {

	SDSIPrincipal nameContext;
	Subject subjects[];
	int k;

	public static final String LABEL = "k-of-n";

	/**
	 * Parse a ThresholdSubject from an S-expression.
	 *
	 * @param sexplist An SexpList to parse into a threshold subject.
	 * @param nameContext the principal to whom any names specified in
	 * the sexplist are meaningful.
	 */
	public ThresholdSubject(SexpList sexplist, SDSIPrincipal nameContext)
		throws SexpParseException {

		super(sexplist);
		this.nameContext = nameContext;
		if (!sexplist.getType().equals(LABEL)) {
			throw new SexpParseException("Not a threshold subject");
		}
		if ((sexplist.size() < 3)
			|| !(sexplist.elementAt(1) instanceof SexpString)
			|| !(sexplist.elementAt(2) instanceof SexpString)) {
			throw new
				SexpParseException("Incorrectly formatted threshold subject");
		}
		String kStr = ((SexpString) sexplist.elementAt(1)).stringContent();
		k = Integer.parseInt(kStr);
		String nStr = ((SexpString) sexplist.elementAt(2)).stringContent();
		int n = Integer.parseInt(nStr);
		int nCheck = sexplist.size()-3;
		if (k<1 || k>n || n!=nCheck) {
			throw new
				SexpParseException("Incorrectly formatted threshold subject "
					+" (k="+k+", n="+n+", count="+nCheck+")");
		}
		subjects = new Subject[n];
		for (int i=0; i<n; i++) {
			SDSIObject obj=null;
			try {
				obj = SDSIObject.principalParse(
					(SexpList) sexplist.elementAt(i+3), nameContext);
			} catch (ClassCastException ex) {
				throw new SexpParseException("Threshold subject entry "
					+(i+1)+" is not even a list");
			}
			try {
				subjects[i] = (Subject) obj;
			} catch (ClassCastException ex) {
				throw new SexpParseException("Threshold subject entry "
					+(i+1)+" is not a Subject, it's a "+obj.getClass());
			}
		}
	}

	/**
	 * Build a threshold principal given existing Subjects.
	 *
	 * @param subjects the list of n Subjects. (n=subjects.length)
	 * @param k the number of subjects that must agree to represent
	 * this principal
	 */
	public ThresholdSubject(Subject[] subjects, int k)
		throws SexpParseException {
		this.subjects = subjects;
		this.k = k;

		SexpList sexplist = (new SexpList())
			.append(new Integer(k).toString())
			.append(new Integer(subjects.length).toString());
		for (int i=0; i<subjects.length; i++) {
			sexplist = sexplist.append(subjects[i].getSrep());
		}
		srep = sexplist;
	}

	/**
	 * Return the total number of principals named in the
	 * ThresholdSubject.
	 */
	public int getN() { return subjects.length; }

	/**
	 * Return the number <em>k</em> of principals that must agree in
	 * order that together they speak for the issuer.
	 */
	public int getK() { return k; }

	public Subject getSubject(int i) {
		return subjects[i];
	}

	public String toShortString() {
		return "threshold subject ("+getK()+"-of-"+getN()+")";
	}
}	
