/* UtilLib - Copyright (c) 2014 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.text.tableparser;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;


/**
 *
 * @author Vincent Calame
 */
public class RowParser {

    private final static int WAITING_FROM_DELIMITER_STEP = 3;
    private final static int WAITING_FROM_VALUE_STEP = 5;
    private final static int VALUE_STEP = 9;
    private final static int QUOTED_VALUE_STEP = 10;
    private final RowHandler rowHandler;
    private final CsvParameters csvParameters;
    private List<String> resultList = new ArrayList<String>();
    private int step = WAITING_FROM_VALUE_STEP;
    private StringBuilder buf = new StringBuilder();
    private boolean previousWhite = false;

    public RowParser(RowHandler rowHandler, CsvParameters csvParameters) {
        this.rowHandler = rowHandler;
        this.csvParameters = csvParameters;
    }

    public void parse(Reader reader) throws IOException {
        boolean lastCompleted = false;
        BufferedReader bufreader = new BufferedReader(reader);
        String line;
        while ((line = bufreader.readLine()) != null) {
            try {
                boolean complete = parseLine(line);
                if (complete) {
                    String[] array = flushCompleteValues();
                    if (array != null) {
                        rowHandler.addRow(array);
                    }
                    lastCompleted = true;
                } else {
                    lastCompleted = false;
                }
            } catch (IllegalArgumentException ise) {
                break;
            }
        }
        if (!lastCompleted) {
            String[] array = flushCompleteValues();
            if (array != null) {
                rowHandler.addRow(array);
            }
        }
    }

    private boolean parseLine(String line) {
        int length = line.length();
        for (int i = 0; i < length; i++) {
            i = parseCharAt(line, i);
        }
        if (step == QUOTED_VALUE_STEP) {
            buf.append('\n');
            return false;
        } else {
            return true;
        }
    }

    private int parseCharAt(String line, int index) {
        char carac = line.charAt(index);
        if (carac == ' ') {
            switch (step) {
                case VALUE_STEP:
                case WAITING_FROM_DELIMITER_STEP:
                    previousWhite = true;
                    break;
                case QUOTED_VALUE_STEP:
                    buf.append(carac);
                    break;
            }
            return index;
        }
        if (carac == csvParameters.getDelimiter()) {
            if (step != QUOTED_VALUE_STEP) {
                flushValue();
                step = WAITING_FROM_VALUE_STEP;
            } else {
                buf.append(carac);
            }
            return index;
        }
        char quoteChar = csvParameters.getQuote();
        boolean escaped = csvParameters.isEscapedCSV();
        switch (step) {
            case WAITING_FROM_DELIMITER_STEP:
            case WAITING_FROM_VALUE_STEP:
                if (carac == quoteChar) {
                    step = QUOTED_VALUE_STEP;
                } else {
                    step = VALUE_STEP;
                    index = parseCharAt(carac, line, index, escaped);
                }
                break;
            case VALUE_STEP:
                if (previousWhite) {
                    buf.append(' ');
                }
                index = parseCharAt(carac, line, index, escaped);
                break;
            case QUOTED_VALUE_STEP:
                if (carac == quoteChar) {
                    if (!escaped) {
                        if (index < (line.length() - 1)) {
                            char next = line.charAt(index + 1);
                            if (next == quoteChar) {
                                buf.append(quoteChar);
                                return index + 1;
                            }
                        }
                    }
                    step = WAITING_FROM_DELIMITER_STEP;
                } else {
                    index = parseCharAt(carac, line, index, escaped);
                }
                break;
        }
        previousWhite = false;
        return index;
    }

    private int parseCharAt(char carac, String line, int index, boolean escaped) {
        if ((escaped) && (carac == '\\')) {
            if (index < (line.length() - 1)) {
                char next = line.charAt(index + 1);
                if (next == 'n') {
                    buf.append('\n');
                } else if (next == 't') {
                    buf.append('\t');
                } else {
                    buf.append(next);
                }
                index = index + 1;
            }
        } else {
            buf.append(carac);

        }
        return index;
    }

    private void flushValue() {
        resultList.add(buf.toString());
        buf = new StringBuilder();
    }

    private String[] flushCompleteValues() {
        if ((buf.length() == 0) && (resultList.isEmpty())) {
            return null;
        }
        flushValue();
        String[] resultArray = resultList.toArray(new String[resultList.size()]);
        resultList.clear();
        step = WAITING_FROM_VALUE_STEP;
        return resultArray;
    }

}
