import java.util.Set;
import java.util.Iterator;
import java.util.HashSet;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

/**
 * This class provides a parser for "sets" of lists -- i.e., for groups of
 * lists of integers, each separated by semicolons, and the whole thing
 * delimited by square brackets.  Specifically, this implements the BNF rule:
 * <pre>
 * 	LISTSET := "[" ( INTLIST ( ";" INTLIST )* )? "]"
 * </pre>
 *
 * @author Terran Lane
 * @version 1.0
 */

public class ListSetParser {
  public static Set parseListSet(Lexer lex)
    throws ParseException, IOException {
    Token t=lex.next();
    Set s=new HashSet();
    if (t==null) {
      // legitimate EOF
      return null;
    }
    if (!t.getTokStr().equals("[")) {
      throw new ParseException("Unxpexted token '" + t + "'; expected '['");
    }
    // discard open bracket
    // if there _is_ an intlist, get it
    ParserSupport.checkEOF(t=lex.next());
    if (t.getTokStr().equals("(")) {
      // start of an INTLIST -- push t back so that it's available for
      // IntListParser to check/verify it
      lex.pushBack(t);
      s.add(IntListParser.parseIntList(lex));
      // now look for optional additional lists
      ParserSupport.checkEOF(t=lex.next());
      while (t.getTokStr().equals(";")) {
	s.add(IntListParser.parseIntList(lex));
	ParserSupport.checkEOF(t=lex.next());
      }
    }
    if (!t.getTokStr().equals("]")) {
      throw new ParseException("Unexpected token '" + t + "'; expected ']'");
    }
    // discard close bracket and return the Set we've constructed
    return s;
  }

  /**
   * Just a small main good for unit testing.  Tries to read a ListSet from
   * each file specified on the command line, then print out the result to
   * stdout.
   */
  public static void main(String[] args) {
    for (int i=0;i<args.length;++i) {
      System.out.println("     ===== " + args[i] + "=====");
      Reader r=null;
      try {
	r=new BufferedReader(new FileReader(args[i]));
	Lexer lex=new WPILexer(r);
	Set s=parseListSet(lex);
	if (s!=null) {
	  _dumpListSet(s);
	}
      }
      catch (IOException e) {
	System.err.println("I/O error while parsing from '" + args[i] +
			   "': " + e);
      }
      catch (ParseException e) {
	System.err.println("Parse error while parsing from '" + args[i] +
			   "': " + e);
      }
      finally {
	// ensure that reader is closed, even if an exception occurs
	if (r!=null) {
	  try {
	    // this is pretty overkill, but close() can
	    // <em>theoretically</em> generate its own bloody IOException.
	    // Ugh.
	    r.close();
	  }
	  catch (IOException e) {
	    System.err.println("Wow.  I/O Exception while trying to " +
			       "close an open file.  Ugh.  Exception=" + e);
	  }
	}
      }
      System.out.println("     ===== " + args[i] + " done =====");
    }
  }

  /* ******************** end of public interface ******************** */

  /**
   * Handy helper function -- just generates a nicely formatted
   * representation of the listSet (that could potentially be parsed back in
   * by ListSetParser).
   *
   * @param s The ListSet as returned by {@link parseListSet()}
   */
  private static void _dumpListSet(Set s) {
    System.out.println("Got list set:");
    System.out.println("\t[");
    for (Iterator si=s.iterator();si.hasNext();) {
      List l=(List)si.next();
      System.out.println("\t\t(");
      for (Iterator li=l.iterator();li.hasNext();) {
	Integer elem=(Integer)li.next();
	System.out.print("\t\t\t" + elem);
	if (li.hasNext()) {
	  System.out.println(",");
	}
	else {
	  System.out.println();
	}
      }
      System.out.println("\t\t)");
      if (si.hasNext()) {
	System.out.println("\t;");
      }
    }
    System.out.println("\t]");
  }
}
