package info.johannesgruber.jgnash.plugin.csvimport.parser;

import info.johannesgruber.jgnash.plugin.csvimport.CsvFile;
import info.johannesgruber.jgnash.plugin.csvimport.CsvRecord;
import info.johannesgruber.jgnash.utils.AccountUtils;
import info.johannesgruber.jgnash.utils.ResourceUtils;

import java.io.IOException;
import java.io.LineNumberReader;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.logging.Logger;

import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;

public class CsvParser {

	public static final String CSV_SEPARATOR = ";";
	private static final Logger LOG = Logger.getLogger(CsvParser.class.getName());

	public static final String KEY_DATE = "date";
	public static final String KEY_AMOUNT = "amount";
	public static final String KEY_MEMO = "memo";

	private Map<String, Integer> columns;

	public CsvFile parse(final LineNumberReader reader) throws IOException,
	    ParseException {
		final CsvFile csvFile = new CsvFile();

		reader.mark(100000);
		String currentLine = reader.readLine();
		columns = getColumNames(currentLine, reader);
		currentLine = reader.readLine();

		while (currentLine != null) {
			csvFile.getRecords().add(parseRecord(currentLine));
			currentLine = reader.readLine();
		}
		return csvFile;
	}

	private CsvRecord parseRecord(String line) throws ParseException {
		List<String> lineParts = Splitter.on(CSV_SEPARATOR)
		    .trimResults(CharMatcher.anyOf("\"")).splitToList(line);

		CsvRecord rec = new CsvRecord();

		// 15.06.2015;"SB-Behebung 83003 Karte3 13.06 um 07:13";13.06.2015;-110,00;EUR;13.06.2015
		// 07:15:13:145;
		LOG.info("Line parts = " + lineParts);
		LOG.info("Date=" + lineParts.get(columns.get(KEY_DATE)) + ", amount="
		    + lineParts.get(columns.get(KEY_AMOUNT)) + ", memo="
		    + lineParts.get(columns.get(KEY_MEMO)));

		try {
			DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT,
			    Locale.getDefault());
			rec.setBookingDate(df.parse(lineParts.get(columns.get(KEY_DATE))));
			Number amount = NumberFormat.getNumberInstance(Locale.getDefault()).parse(
			    lineParts.get(columns.get(KEY_AMOUNT)));
			rec.setAmount(BigDecimal.valueOf(amount.doubleValue()));
		} catch (NumberFormatException nfe) {
			LOG.warning("Could not parse the amount ["
			    + lineParts.get(columns.get(KEY_AMOUNT)) + "], set default value ZERO");
			rec.setAmount(BigDecimal.ZERO);
		} catch (ParseException pe) {
			LOG.warning("Could not parse the date ["+lineParts.get(columns.get(KEY_DATE))+"], set default value");
			rec.setBookingDate(new Date());
		}

		rec.setMemo(lineParts.get(columns.get(KEY_MEMO)));

		return rec;
	}

	private Map<String, Integer> getColumNames(String headline, LineNumberReader reader) throws IOException {
		final ResourceBundle rb = ResourceUtils.getBundle();
		Map<String, Integer> map = new HashMap<String, Integer>();

		Iterable<String> columns = Splitter.on(CSV_SEPARATOR).trimResults()
		    .trimResults(CharMatcher.anyOf("\"")).split(headline);

		int i = 0;
		Iterator<String> colIter = columns.iterator();
		while (colIter.hasNext()) {
			String col = colIter.next();
			if (col.equals(rb.getString("ImportCsv.Column.Date"))) {
				map.put(KEY_DATE, Integer.valueOf(i));
			} else if (col.equals(rb.getString("ImportCsv.Column.Memo"))) {
				map.put(KEY_MEMO, Integer.valueOf(i));
			} else if (col.equals(rb.getString("ImportCsv.Column.Amount"))) {
				map.put(KEY_AMOUNT, Integer.valueOf(i));
			} else {
				LOG.fine("Column name [" + col + "] is not supported, ignore it");
			}
			i++;
		}

		/*
		 * Fallback if the columns are not named correctly. This format is used by
		 * Raiffeisen ELBA.
		 */
		if (map.size() < 3) {
			LOG.info("The headline is not correct, use the default order of columns");
			map.put(KEY_DATE, 2);
			map.put(KEY_AMOUNT, 3);
			map.put(KEY_MEMO, 1);
			reader.reset();
		}

		return map;
	}
}
