/**
   * Reads query file and parses the queries.
   *
   * @requires querystream, outstream, are successfully initialized
   * @requires g to be already generated
   * @param queryStream file input stream of query file
   * @param outStream file output stream of output file
   * @param g
   * @effects writes results to output file
   */
  private static void parseQuery(FileInputStream queryStream, FileOutputStream outStream, Graph g) {

    try {
      BufferedWriter output = new BufferedWriter(new OutputStreamWriter(outStream));
      BufferedReader queryReader = new BufferedReader(new InputStreamReader(queryStream));

      final int COMMAND_INDEX = 0;
      final int U1_INDEX = 1;
      final int U2_INDEX = 2;

      final String QUERY_ENDING = " ?";

      Set<Set<String>> queries = new LinkedHashSet<Set<String>>();

      String line;

      while ((line = queryReader.readLine()) != null) {

        // each query set contains two user id strings and a command
        // this also handles duplicate user ids with different commands
        Set<String> query = new LinkedHashSet<String>();

        // a bit overkill, but eliminate any unnecessary whitespace if needed and replaces with
        // single space
        String[] columns = line.trim().replaceAll("\\s+", " ").split(" ");

        // first column is query
        // second column is user 1
        // third column is user 2

        String command = columns[COMMAND_INDEX];
        String id1 = columns[U1_INDEX];
        String id2 = columns[U2_INDEX];
        query.add(id1);
        query.add(id2);
        query.add(command);

        // check if query ends with question mark (with leading space) and query is unique
        if (line.endsWith(QUERY_ENDING) && !queries.contains(query)) {

          queries.add(query);

          Vertex u1 = new Vertex(id1);
          Vertex u2 = new Vertex(id2);

          printResults(output, g, u1, u2, command);
        }
      }
      queryReader.close();
      output.close();
      // return new LinkedHashMap<List<Vertex>, String>(queries);

    } catch (Exception e) { // if something goes wrong
      throw new RuntimeException(e);
    }
  }
  /**
   * Helper method for parseQuery. Writes the results of the queries to the output file.
   *
   * @param output initialized BufferedWriter for the output file
   * @param g initialized graph of edges and vertices
   * @param u1 one vertex used in commands
   * @param u2 another vertex used in commands
   * @param command command to execute
   */
  private static void printResults(
      BufferedWriter output, Graph g, Vertex u1, Vertex u2, String command) {
    final String commonInfluencers = "commonInfluencers";
    final String numRetweets = "numRetweets";

    final String VERTEX_NOT_FOUND_ERROR =
        "ERROR: One or more vertices does not exist in the graph.";
    final String INVALID_COMMAND_ERROR = "\tError: invalid command ";
    final String PATH_NOT_FOUND_ERROR = "\tPath not found.";

    List<Vertex> allVertices = new ArrayList<Vertex>(g.getVertices());

    try {
      output.write("query: " + command + " " + u1.toString() + " " + u2.toString());
      output.newLine();
      output.write("<result>");
      output.newLine();

      // check if vertices exist in graph
      if (!allVertices.contains(u1) || !allVertices.contains(u2)) {
        output.write(VERTEX_NOT_FOUND_ERROR);
        output.newLine();
        output.write("</result>");
        output.newLine();
        output.newLine();
        return;
      }
      // if query is commonInfluencers
      if (command.equals(commonInfluencers)) {
        List<Vertex> commonFollowers =
            new LinkedList<Vertex>(Algorithms.commonDownstreamVertices(g, u1, u2));
        for (Vertex v : commonFollowers) {
          output.write("\t" + v.toString());
          output.newLine();
        }
      }

      // if query is numRetweets
      else if (command.equals(numRetweets)) {
        // note switch in u1 and u2; this is because tweets go upstream
        int distance = Algorithms.shortestDistance(g, u2, u1);

        if (distance == -1) {
          output.write(PATH_NOT_FOUND_ERROR);
        } else {
          // implicitly convert distance to string as printing out ints somehow didn't work
          output.write("" + distance);
          // System.out.println(distance);
        }

        output.newLine();
      } else {
        output.write(INVALID_COMMAND_ERROR + command);
        output.newLine();
      }
      output.write("</result>");
      output.newLine();
      output.newLine();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }