package org.openlca.core.matrix.io;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.function.Consumer;

import org.openlca.core.matrix.format.ByteMatrixReader;
import org.openlca.core.matrix.format.MatrixReader;

public final class Csv {

	private final DecimalFormat numberFormat;
	private String delimiter = ",";
	private Charset charset = StandardCharsets.UTF_8;

	public Csv() {
		numberFormat = (DecimalFormat) NumberFormat.getInstance(Locale.US);
		numberFormat.setMaximumFractionDigits(1000);
	}

	public static Csv defaultConfig() {
		return new Csv();
	}

	public String quote(String s) {
		if (s == null)
			return "\"\"";
		return s.contains("\"")
			? "\"" + s.replace("\"", "\"\"") + "\""
			: "\"" + s + "\"";
	}

	public Csv withDelimiter(String delimiter) {
		if (delimiter != null) {
			this.delimiter = delimiter;
		}
		return this;
	}

	public Csv withEncoding(Charset charset) {
		if (charset != null) {
			this.charset = charset;
		}
		return this;
	}

	public Csv withDecimalSeparator(char separator) {
		var symbols = new DecimalFormat().getDecimalFormatSymbols();
		symbols.setDecimalSeparator(separator);
		symbols.setGroupingSeparator(separator == ',' ? '.' : ',');
		numberFormat.setDecimalFormatSymbols(symbols);
		return this;
	}

	public void write(double[] vector, File file) {
		if (vector == null || file == null)
			return;
		writer(file, w -> {
			for (double v : vector) {
				writeln(w, numberFormat.format(v));
			}
		});
	}

	public void write(MatrixReader matrix, File file) {
		if (matrix == null || file == null)
			return;
		var buffer = new String[matrix.columns()];
		writer(file, w -> {
			for (int row = 0; row < matrix.rows(); row++) {
				for (int col = 0; col < matrix.columns(); col++) {
					var val = matrix.get(row, col);
					buffer[col] = val == 0
						? "0"  // avoid slow formatter calls
						: numberFormat.format(val);
				}
				writeln(w, line(buffer));
			}
		});
	}

	public void write(ByteMatrixReader matrix, File file) {
		if (matrix == null || file == null)
			return;
		var buffer = new String[matrix.columns()];
		writer(file, w -> {
			for (int row = 0; row < matrix.rows(); row++) {
				for (int col = 0; col < matrix.columns(); col++) {
					buffer[col] = Integer.toString(matrix.get(row, col));
				}
				writeln(w, line(buffer));
			}
		});
	}

	void writeln(BufferedWriter writer, String line) {
		try {
			writer.write(line);
			writer.newLine();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	void writer(File file, Consumer<BufferedWriter> fn) {
		try (var stream = new FileOutputStream(file);
				 var writer = new OutputStreamWriter(stream, charset);
				 var buffer = new BufferedWriter(writer)) {
			fn.accept(buffer);
			buffer.flush();
		} catch (IOException e) {
			throw new RuntimeException("Failed to write file: " + file.getName(), e);
		}
	}

	String line(String[] entries) {
		if (entries == null)
			return "";
		var b = new StringBuilder();
		for (int i = 0; i < entries.length; i++) {
			if (i != 0) {
				b.append(delimiter);
			}
			var e = entries[i];
			if(e != null) {
				b.append(e);
			}
		}
		return b.toString();
	}
}

@@ -522,22 +522,16 @@
 private 
-final 
 DecimalF
@@ -639,145 +639,8 @@
 8;%0A%0A
-%09public Csv() %7B%0A%09%09numberFormat = (DecimalFormat) NumberFormat.getInstance(Locale.US);%0A%09%09numberFormat.setMaximumFractionDigits(1000);%0A%09%7D%0A%0A
 %09pub
@@ -1162,108 +1162,285 @@
 %7B%0A%09%09
-var symbols = new DecimalFormat().getDecimalFormatSymbols(
+if (separator == '.') %7B%0A%09%09%09numberFormat = null;%0A%09%09%09return this;%0A%09%09%7D%0A%09%09numberFormat = (DecimalFormat) NumberFormat.getInstance(Locale.US);%0A%09%09numberFormat.setMaximumFractionDigits(1000);%0A%09%09numberFormat.setGroupingUsed(false
 );%0A%09%09
+var 
 symbols
-.setDecimalSeparator(separator
+ = numberFormat.getDecimalFormatSymbols(
 );%0A%09
@@ -1451,24 +1451,23 @@
 bols.set
-Grouping
+Decimal
 Separato
@@ -1481,27 +1481,8 @@
 ator
- == ',' ? '.' : ','
 );%0A%09
@@ -1712,29 +1712,16 @@
 teln(w, 
-numberFormat.
 format(v
@@ -2023,18 +2023,29 @@
 %09%09%09%09
-var val = 
+buffer%5Bcol%5D = format(
 matr
@@ -2064,84 +2064,180 @@
 col)
+)
 ;%0A%09%09%09%09
-%09buffer%5Bcol%5D = val == 0%0A%09%09%09%09%09%09? %220%22  // avoid slow formatter ca
+%7D%0A%09%09%09%09writeln(w, line(buffer));%0A%09%09%09%7D%0A%09%09%7D);%0A%09%7D%0A%0A%09private String format(double v) %7B%0A%09%09if (v == 0)%0A%09%09%09return %220%22;%0A%09%09return numberFormat == nu
 ll
-s
 %0A%09%09%09
+? Double.toString(v)%0A
 %09%09%09:
@@ -2262,57 +2262,8 @@
 at(v
-al);%0A%09%09%09%09%7D%0A%09%09%09%09writeln(w, line(buffer));%0A%09%09%09%7D%0A%09%09%7D
 );%0A%09

there is an error in the matrix export