/*
 * Copyright (c) 2009, Takeyuki Nagao
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 * 
 *  * Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package jp.sourceforge.dvibrowser.dvicore.util.csv;
import java.util.ArrayList;
import java.util.List;
public class CsvLineParser<K, V> {
  private int state = 0;
  private StringBuilder sb = null;
  private List<String> line = null;
  private List<List<String>> lines = new ArrayList<List<String>>();
  
  public String [][] getData()
  {
    int cols=0, rows=lines.size();
    for (List<String> line : lines) {
      cols = Math.max(cols, line.size());
    }
    String [] [] ret = new String[rows][cols];
    int i=0;
    for (List<String> line : lines) {
      int j=0;
      for (String s : line) {
        ret[i][j] = s;
        j++;
      }
      i++;
    }
    return ret;
  }
  
  public void feed(char c)
  throws CsvException
  {
    if (state == 0 || state == 1) {
      // Not inside a quotation.
      if (state == 0) {
        // Begin of Line
        openLine();
      } else {
        if (sb == null) {
          openItem();
        }
      }
      if (isQuoteChar(c)) {
        state = 2;
      } else if (isSeparator(c)) {
        closeItem();
        state = 1;
      } else if (isNewLine(c)) {
        closeLine();
        state = 0;
      } else {
        shipout(c);
        state = 1;
      }
    } else if (state == 2) {
      // Inside a quotation.  Last char is not a quotation.
      if (isQuoteChar(c)) {
        state = 3;
      } else {
        shipout(c);
      }
    } else if (state == 3) {
      // Inside a quotation.  Last char is a quotation.
      if (isQuoteChar(c)) {
        shipout(c);
        state = 2;
      } else if (isSeparator(c)) {
        closeItem();
        state = 1;
      } else if (isNewLine(c)) {
        closeLine();
        state = 0;
      } else {
        throw new CsvException("Illegal character: " + c);
      }
    } else if (state == 4) {
      throw new IllegalStateException("Parser is already closed");
    } else {
      throw new IllegalStateException("state=" + state);
    }
  }
  
  public void close()
  throws CsvException
  {
    if (state == 2) {
      throw new CsvException("Input ended while parsing quotation.");
    } else if (state == 3) {
      closeLine();
    }
    state = 4;
  }
  
  protected void openLine() {
    line = new ArrayList<String>();
    openItem();
  }

  protected void closeLine() {
    closeItem();
    lines.add(line);
  }
  
  protected void openItem() {
    sb = new StringBuilder();
  }
  
  protected void closeItem() {
    line.add(sb.toString());
    sb = null;
  }

  private boolean isNewLine(char c) {
    return c == '\n' || c == '\r';
  }

  private void shipout(char c) {
    sb.append(c);
  }
  
  // TODO: outsource the quote char.
  private boolean isQuoteChar(char c) {
    return (c == '"');
  }
  
  private boolean isSeparator(char c) {
    return (c == ',');
  }
}