/*
 * Copyright 2008-2019 by Emeric Vernat
 *
 *     This file is part of Java Melody.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.bull.javamelody.swing.print;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Locale;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTable;

import net.bull.javamelody.I18NAdapter;
import net.bull.javamelody.swing.table.MBasicTable;

/**
 * Writer CSV non localisé.
 *
 * @author Emeric Vernat
 */
public class MCsvWriter extends MPrinter {
	/** Caractère de séparation dans les fichiers csv (non local). */
	public static final char CSV_SEPARATOR = ',';

	/**
	 * Icône Excel.
	 */
	public static final ImageIcon MSEXCEL_ICON = new ImageIcon(
			MCsvWriter.class.getResource("/icons/ms excel.png"));

	/** {@inheritDoc} */
	@Override
	public void print(final MBasicTable table, final OutputStream out) throws IOException {
		writeCsv(table, out);
	}

	/** {@inheritDoc} */
	@Override
	public String getName() {
		return I18NAdapter.getString("export_csv");
	}

	/** {@inheritDoc} */
	@Override
	public String getFileExtension() {
		return "csv";
	}

	/** {@inheritDoc} */
	@Override
	public Icon getIcon() {
		return MSEXCEL_ICON;
	}

	/**
	 * Encode un texte pour l'export au format csv ou csv local.
	 *
	 * @return String
	 * @param text
	 *           String
	 * @param csvSeparator
	 *           char
	 */
	protected String formatCsv(final String text, final char csvSeparator) {
		String result = text;
		if (result.indexOf('\n') != -1) {
			// le retour chariot fonctionne dans les dernières versions d'excel entre des doubles quotes
			// mais il ne fonctionne pas dans OpenOffice 1.1.2
			result = result.replace('\n', ' ');
		}
		int index = result.indexOf('"');
		while (index != -1) {
			// on double les double-quote pour csv (non performant mais rare)
			result = new StringBuilder(result).insert(index, '"').toString();
			index = result.indexOf('"', index + 2);
		}
		if (text.indexOf(csvSeparator) != -1 || text.indexOf('"') != -1) {
			final String tmp = '"' + result + '"';
			result = tmp;
		}
		return result;
	}

	/**
	 * Exporte une JTable dans un fichier au format csv pour double-clique. (séparateur ',', formats nombres et dates US).
	 *
	 * @param table
	 *           MBasicTable
	 * @param outputStream
	 *           OutputStream
	 * @throws IOException
	 *            Erreur disque
	 */
	protected void writeCsv(final MBasicTable table, final OutputStream outputStream)
			throws IOException {
		writeCsv(table, outputStream, DateFormat.getDateInstance(DateFormat.SHORT, Locale.US));
	}

	/**
	 * Exporte une JTable dans un fichier au format csv pour double-clique. (séparateur ',', formats nombres et dates US).
	 *
	 * @param table
	 *           MBasicTable
	 * @param outputStream
	 *           OutputStream
	 * @param dateFormat
	 *           DateFormat
	 * @throws IOException
	 *            Erreur disque
	 */
	protected void writeCsv(final MBasicTable table, final OutputStream outputStream,
			final DateFormat dateFormat) throws IOException {
		// récupération des informations utiles
		final int columnCount = table.getColumnModel().getColumnCount();
		final int rowCount = table.getRowCount();
		final String charset = System.getProperty("file.encoding");
		final Writer out = new OutputStreamWriter(outputStream, charset);

		final char csvSeparator = CSV_SEPARATOR;
		// le séparateur des .csv (',' à l'américaine)
		final NumberFormat decimalFormat = NumberFormat.getInstance(Locale.US);
		decimalFormat.setGroupingUsed(false);
		final String eol = System.getProperty("line.separator");

		// titres des colonnes
		writeCsvHeader(table, out, csvSeparator);

		// les données proprement dites (ligne par ligne puis colonne par colonne)
		Object value;
		String text;
		for (int k = 0; k < rowCount; k++) {
			for (int i = 0; i < columnCount; i++) {
				value = getValueAt(table, k, i);

				if (value instanceof Number) {
					text = decimalFormat.format(value);
				} else if (value instanceof Date) {
					text = dateFormat.format(value);
				} else if (value instanceof Boolean) {
					text = value.toString();
				} else {
					text = getTextAt(table, k, i);
					text = formatCsv(text, csvSeparator);
				}

				out.write(text);
				if (i < columnCount - 1) {
					out.write(csvSeparator);
				}
			}
			if (k < rowCount - 1) {
				out.write(eol);
			}
		}
		out.flush();
	}

	/**
	 * Exporte les headers csv d'une JTable..
	 *
	 * @param table
	 *           JTable
	 * @param out
	 *           Writer
	 * @param csvSeparator
	 *           char
	 * @throws IOException
	 *            Erreur disque
	 */
	protected void writeCsvHeader(final JTable table, final Writer out, final char csvSeparator)
			throws IOException {
		// récupération des informations utiles
		final int columnCount = table.getColumnModel().getColumnCount();

		final String eol = System.getProperty("line.separator");
		if (table.getName() != null) {
			String title = formatCsv(table.getName(), csvSeparator);
			if (title.startsWith("ID")) {
				final String tmp = '"' + title + '"';
				title = tmp;
			}
			out.write(title);
			out.write(eol);
		}
		// titres des colonnes
		String text;
		for (int i = 0; i < columnCount; i++) {
			text = String.valueOf(table.getColumnModel().getColumn(i).getHeaderValue());
			text = formatCsv(text, csvSeparator);
			if (i == 0 && text.startsWith("ID")) {
				out.write('"' + text + '"'); // si commence par ID, Excel pense que c'est un sylk !
			} else {
				out.write(text);
			}
			if (i < columnCount - 1) {
				out.write(csvSeparator);
			}
		}
		out.write(eol);
	}
}
