package javacodebook.io.csv;

import java.io.*;
import java.util.*;

/**
 * Eine Klasse, mit der mit einem Trennzeichen separierte Werte eingelesen
 * werden können. Für jede Zeile werden die Werte in einem Hashtable abgelegt.
 * Eine Klasse, die die Daten weiterverarbeiten soll, kann dann unter dem
 * Namen der jeweiligen Spalte die Daten aus dem Hashtable auslesen.
 */
public class CSVReader {

    private char delimiter;
    private BufferedReader reader;
    private Vector header;
    private String nextLine;

    public CSVReader(Reader reader, char delimiter) {
        this.delimiter = delimiter;
        this.reader = new BufferedReader(reader);
        this.header = readHeader();
        nextLine = null;
    }

    public Vector getHeader() {
        return header;
    }

    public boolean hasMoreLines() {
        try {
            if (nextLine == null || nextLine.trim().equals(""))
                nextLine = reader.readLine();
        }
        catch (Exception ignore)
        {}

        if (nextLine == null || nextLine.trim().equals("")) {
            close();
            return false;
        }
        else
            return true;
    }

    public Hashtable getNextLine() {
        // Liest auf jeden Fall die neue Zeile, wenn es eine gibt.
        if (!hasMoreLines())
            return null;

        Hashtable hash = new Hashtable();

        // Aus der Zeile wird die Hashtable erzeugt.
        Vector dataFields = parseLine(nextLine.trim());
        for (int i=dataFields.size()-1; i>=0; i--)
            hash.put(header.elementAt(i), dataFields.elementAt(i));

        // Löscht die Zeile, damit hasMoreLines auf jeden Fall
        // eine neue Zeile einliest.
        nextLine = null;

        return hash;
    }

    public void close() {
        try {
            reader.close();
        }
        //kann ignoriert werden, da hasMoreLines den Reader bereits schliesst
        catch(IOException ignored)
        {}
    }

    private Vector readHeader() {
        Vector header = null;
        try {
            String line = reader.readLine();
            header = parseLine(line);
        }
        catch(Exception e) {
            e.printStackTrace(System.out);
        }
        return header;
    }

    private Vector parseLine(String line) {
        Vector fields = new Vector();
        boolean quote = false;
        int start = 0, end = 0, index = 0, max = line.length()-1;
        try {
            // Alle Spalten durchlaufen
            while (index <= max) {
                start = index;
                quote = false;

                //Inhalt einer Spalte extrahieren
                while (index <= max) {
                    char check = line.charAt(index);
                    //Nun wird der nächste Delimiter gesucht, der NICHT
                    //innerhalb von Anführungszeichen (") steht. Wenn ein
                    //Anführungszeichen gefunden wurde, dann muss der Merker
                    //getoggled werden.
                    if (check == '"')
                        quote = !quote;
                    //Es befindet sich auf jeden Fall eine gerade Anzahl von
                    //"-Zeichen zwischen den Quotes, so dass nur die am Anfang
                    //und Ende berücksichtigt werden.
                    else if (check == delimiter && quote == false)
                        break;
                    index++;
                }
                end = index;

                // Anführungszeichen am Anfang und am Ende gehören nicht zum
                // String und werden deshalb auch nicht beachtet.
                if (line.charAt(start) == '"' && line.charAt(end-1) == '"') {
                    start++;
                    end--;
                }

                // Der gefundene Text wird in dem Vector gespeichert, ohne jedoch zu
                // vergessen, dass zwei auf einander folgende Anführungszeichen ("")
                // durch ein Einzelnes (") zu ersetzen sind.
                fields.addElement(Toolbox.replace(line.substring(start, end), "\"\"", "\""));
                index++;
            }
        }
        catch(Exception e) {
            e.printStackTrace(System.out);
        }
        return fields;
    }
}