/**
   * Main method.
   *
   * @param args
   * @throws IOException
   */
  public static void main(String[] args) throws IOException {

    Scanner scanner = new Scanner(System.in);
    String newInput = " ";
    File f = null;
    boolean loopForFileName = true;
    boolean loopForFileLocation = true;
    // Looping to get correct input
    while (loopForFileLocation == true) {

      while (loopForFileName == true) {
        System.out.print("Please enter a filename: ");
        newInput = scanner.nextLine();
        if (newInput.length() == 0) {
          System.out.println("");
          System.out.println("You must enter a file name. ");
          System.out.println("");
        } else {
          loopForFileName = false;
        }
      }

      File file = new File(newInput);
      if (file.exists() == false) {
        System.out.println("");
        System.out.println("The file you entered was not found.");
        System.out.println("");
        loopForFileName = true;
      } else {
        loopForFileLocation = false;
        f = file;
      }
    }
    scanner.close();

    System.out.println("");

    // Character is the Key and Symbol is the Value
    HashMap<Character, Symbol> Letters = new HashMap<Character, Symbol>();
    String line;
    BufferedReader reader = new BufferedReader(new FileReader(f));
    // Reading data and putting it into the HashMap
    while ((line = reader.readLine()) != null) {

      for (int i = 0; i < line.length(); i++) {

        char c = line.charAt(i);
        Symbol s = Letters.get(c);
        if (s == null) {
          s = new Symbol(c, 0);
          Letters.put(c, s);
        }
        s.addToFrequency();
      }
    }
    reader.close();

    ArrayHeap<Node> arrayHeap = new ArrayHeap<Node>();
    // Adding to the ArrayHeap of Nodes
    for (Symbol s : Letters.values()) {
      Node n = new Node();
      n.addSymbols(s);
      arrayHeap.add(n);
    }

    ArrayHeap<Node> arrayOfNodes = arrayHeap;
    Node results = new Node();

    int counter = 0;
    // Calculating code words using Nodes
    while (arrayOfNodes.isEmpty() == false) {

      Node rightNode = arrayOfNodes.removeMin();
      if (arrayOfNodes.isEmpty()) {

        if (counter == 0) {
          rightNode.createNewCodeWord("1");
        }
        results = rightNode;
      } else {
        Node leftNode = arrayOfNodes.removeMin();
        rightNode.createNewCodeWord("0");
        leftNode.createNewCodeWord("1");
        leftNode.addNode(rightNode);
        arrayOfNodes.add(leftNode);
      }
      counter++;
    }
    // Displaying output
    System.out.println("Variable Length Code Output");
    System.out.println("____________________________________________________");
    for (Symbol s : results.getSymbols()) {

      System.out.printf(
          "Symbol: %3s  Codeword: %10s  Frequency: %5d",
          s.getCharacter(), s.getCodeWord(), s.getFrequency());
      System.out.print("\n");
    }

    System.out.println("");
    System.out.println(
        "Average VLC codeword length: "
            + String.format("%.2f", results.getAvgVlcLength())
            + " bits per symbol");

    System.out.println(
        "Average Fixed length codeword length: "
            + String.format("%.2f", results.getAvgFixedLength())
            + " bits per symbol");
    System.out.println("");
  }