package drexel.info634.amazon.parser;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.List;

import javax.naming.directory.InvalidAttributesException;

import drexel.info634.amazon.parser.dto.ProductDTO;
import drexel.info634.amazon.parser.output.ConsoleOutput;
import drexel.info634.amazon.parser.output.Output;

/**
 * Parses Amazon product metadata text file into Java objects.<br> See <a
 * href="https://snap.stanford.edu/data/amazon-meta.html">Data Source</a><br> <br> Created by Rob
 * Wise on 4/15/2015. <br>
 */
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public class Parser {

  private final static Charset ENCODING = StandardCharsets.UTF_8;
  private final static int DATA_START_LINE = 3;

  public static void main(String... args) throws IOException {
    Parser parser = new Parser("resources/amazon-meta.txt", new ConsoleOutput());
    parser.parse();
  }

  private final Path amazonData;
  private final Output output;
  private final ProductFactory fProduct;

  private int lineLimit;
  private int lineNumber;
  private int productCount;
  private String line;
  private BufferedReader reader;

  public Parser(String pathString, Output output) {
    this.amazonData = Paths.get(pathString);
    this.output = output;
    fProduct = new ProductFactory();
  }

  public void parse(int lineLimit) throws IOException {
    try (BufferedReader reader = Files.newBufferedReader(amazonData, ENCODING)) {

      initializeFields(lineLimit, reader);

      System.out.println("Beginning Parse...");
      System.out.println();

      // Connect to Output
      output.open();

      moveReaderToDataStart(reader);

      // Parse data into ProductDTOs
      List<String> productLines = new ArrayList<>();

      while (lineLimit < 0 || ++lineNumber <= lineLimit) {
        line = reader.readLine();

        // Check for end of data
        if (readerAtEndOfData()) {
          outputProduct(productLines);
          break;
        }

        // Normalize non-regex-parse-able characters
        line = Normalizer.normalize(line, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
        if (readerAtEndOfProduct()) {
          outputProduct(productLines);
          productLines = new ArrayList<>();
        } else {
          productLines.add(line);
        }
      }
    } catch (InvalidAttributesException e) {
      System.out.println("Bad data on line: " + lineNumber);
      System.out.println("Line reads: " + line);
      e.printStackTrace();
    } finally {
      output.close();
    }
    System.out.println("Parse complete: " + productCount + " Products Created...");
  }

  private boolean readerAtEndOfData() {
    return null == line || lineNumber == lineLimit;
  }

  private void moveReaderToDataStart(BufferedReader reader) throws IOException {
    // Skip past beginning lines
    while (lineNumber < DATA_START_LINE) {
      reader.readLine();
      lineNumber++;
    }
  }

  private void initializeFields(int lineLimit, BufferedReader reader) {
    this.lineLimit = lineLimit;
    this.reader = reader;
    lineNumber = 0;
    productCount = 0;
    line = null;
  }

  private void outputProduct(List<String> productLines)
      throws InvalidAttributesException, IOException {
    String[] productLinesArray = productLines.toArray(new String[productLines.size()]);
    ProductDTO product = fProduct.build(productLinesArray);
    output.createProduct(product);
    productCount++;
    if (productCount % 1000 == 0) {
      System.out.println("***Status: " + productCount + " Products Created...");
    }
  }

  public void parse() throws IOException {
    parse(-1);
  }

  public String getFirstLines(int numLines) throws IOException {
    String lines = "";
    try (BufferedReader reader = Files.newBufferedReader(amazonData, ENCODING)) {
      String line;
      int lineCount = 0;
      while ((line = reader.readLine()) != null && lineCount < numLines) {
        lines += System.lineSeparator() + line;
        lineCount++;
      }
    }
    return lines;
  }

  private boolean readerAtEndOfProduct() {
    return line.isEmpty();
  }

}

@@ -621,22 +621,25 @@
 cts.
-%3Cbr%3E
+%0A * %3Cp%3E%0A *
  See %3Ca
-%0A *
  hre
@@ -709,17 +709,18 @@
 %3C/a%3E
-%3Cbr%3E %3Cbr%3E
+%0A * %3Cp%3E%0A *
  Cre
@@ -730,19 +730,16 @@
 d by Rob
-%0A *
  Wise on
@@ -753,13 +753,8 @@
 015.
- %3Cbr%3E
 %0A */
@@ -942,179 +942,8 @@
  3;%0A
-%0A  public static void main(String... args) throws IOException %7B%0A    Parser parser = new Parser(%22resources/amazon-meta.txt%22, new ConsoleOutput());%0A    parser.parse();%0A  %7D%0A%0A
   pr
@@ -1043,17 +1043,16 @@
 roduct;%0A
-%0A
   privat
@@ -1178,17 +1178,16 @@
 reader;%0A
-%0A
   public
@@ -1335,32 +1335,265 @@
 Factory();%0A  %7D%0A%0A
+  public static void main(String... args) throws IOException %7B%0A    Parser parser = new Parser(%22resources/amazon-meta.txt%22, new ConsoleOutput());%0A    parser.parse();%0A  %7D%0A%0A  public void parse() throws IOException %7B%0A    parse(-1);%0A  %7D%0A%0A
   public void pa
@@ -2911,88 +2911,185 @@
 ate 
-boolean readerAtEndOfData() %7B%0A    return null == line %7C%7C lineNumber == lineLimit
+void initializeFields(int lineLimit, BufferedReader reader) %7B%0A    this.lineLimit = lineLimit;%0A    this.reader = reader;%0A    lineNumber = 0;%0A    productCount = 0;%0A    line = null
 ;%0A  
@@ -3318,185 +3318,88 @@
 ate 
-void initializeFields(int lineLimit, BufferedReader reader) %7B%0A    this.lineLimit = lineLimit;%0A    this.reader = reader;%0A    lineNumber = 0;%0A    productCount = 0;%0A    line = null
+boolean readerAtEndOfData() %7B%0A    return null == line %7C%7C lineNumber == lineLimit
 ;%0A  
@@ -3850,60 +3850,72 @@
 %0A  p
-ublic void parse() throws IOException %7B%0A    parse(-1
+rivate boolean readerAtEndOfProduct() %7B%0A    return line.isEmpty(
 );%0A 
@@ -4286,24 +4286,24 @@
     %7D%0A    %7D%0A
+
     return l
@@ -4317,81 +4317,6 @@
  %7D%0A%0A
-  private boolean readerAtEndOfProduct() %7B%0A    return line.isEmpty();%0A  %7D%0A%0A
 %7D%0A

fix formatting of class comment in Parser.java