/* FichothequeLib_Tools - Copyright (c) 2015-2019 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.fichotheque.tools.exportation.table;

import java.io.IOException;
import java.text.DecimalFormatSymbols;
import java.util.List;
import net.fichotheque.Subset;
import net.fichotheque.exportation.table.ColDef;
import net.fichotheque.format.SourceLabelProvider;
import net.fichotheque.utils.TableDefUtils;
import net.mapeadores.util.exceptions.NestedIOException;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.UserLangContext;
import net.mapeadores.util.money.Amount;
import net.mapeadores.util.primitives.Decimal;
import net.mapeadores.util.date.FuzzyDate;
import net.mapeadores.util.table.TableWriter;


/**
 *
 * @author Vincent Calame
 */
public class CsvTableWriter implements TableWriter {

    private final ColDef[] colDefArray;
    private final Appendable appendable;
    private final DecimalFormatSymbols symbols;
    private final boolean[] hiddenArray;
    private int rowNumber;
    private int columnNumber;
    private boolean firstCell;

    public CsvTableWriter(List<ColDef> colDefList, Appendable appendable, UserLangContext userLangContext) {
        this.appendable = appendable;
        this.symbols = new DecimalFormatSymbols(userLangContext.getFormatLocale());
        this.rowNumber = 0;
        int colLength = colDefList.size();
        this.colDefArray = new ColDef[colLength];
        this.hiddenArray = new boolean[colLength];
        int p = 0;
        for (ColDef colDef : colDefList) {
            colDefArray[p] = colDef;
            if (colDef.isFormula()) {
                hiddenArray[p] = true;
            } else {
                hiddenArray[p] = false;
            }
            p++;
        }
    }


    public void appendColumnTitleHeader(Lang workingLang, SourceLabelProvider sourceLabelProvider, Subset referenceSubset) {
        if (rowNumber > 0) {
            appendNewLine();
        }
        rowNumber++;
        try {
            int colLength = colDefArray.length;
            boolean next = false;
            for (int i = 0; i < colLength; i++) {
                ColDef colDef = colDefArray[i];
                if (hiddenArray[i]) {
                    continue;
                }
                if (next) {
                    appendable.append(",");
                } else {
                    next = true;
                }
                appendable.append("\"");
                String colTitle = TableDefUtils.getColTitle(colDef, workingLang, sourceLabelProvider, referenceSubset);
                int length = colTitle.length();
                for (int j = 0; j < length; j++) {
                    char carac = colTitle.charAt(j);
                    switch (carac) {
                        case '\"':
                            appendable.append("\"\"");
                            break;
                        default:
                            appendable.append(carac);
                    }
                }
                appendable.append("\"");
            }
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }


    public void appendColumnKeyHeader() {
        if (rowNumber > 0) {
            appendNewLine();
        }
        rowNumber++;
        try {
            int colLength = colDefArray.length;;
            boolean next = false;
            for (int i = 0; i < colLength; i++) {
                ColDef colDef = colDefArray[i];
                if (hiddenArray[i]) {
                    continue;
                }
                if (next) {
                    appendable.append(",");
                } else {
                    next = true;
                }
                appendable.append(colDef.getColName());
            }
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    @Override
    public int startRow() {
        if (rowNumber > 0) {
            appendNewLine();
        }
        rowNumber++;
        columnNumber = 0;
        firstCell = true;
        return rowNumber;
    }

    @Override
    public int addIntegerCell(Long lg) {
        if (!testCell()) {
            return columnNumber;
        }
        if (lg != null) {
            try {
                appendable.append(String.valueOf(lg));
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
        }
        return columnNumber;
    }

    @Override
    public int addDecimalCell(Decimal decimal) {
        if (!testCell()) {
            return columnNumber;
        }
        if (decimal != null) {
            try {
                appendable.append(String.valueOf(decimal.toString()));
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
        }
        return columnNumber;
    }

    @Override
    public int addStringCell(String s) {
        if (!testCell()) {
            return columnNumber;
        }
        if (s != null) {
            startQuote();
            int length = s.length();
            try {
                for (int i = 0; i < length; i++) {
                    char carac = s.charAt(i);
                    switch (carac) {
                        case '\"':
                            appendable.append("\"\"");
                            break;
                        case '\r':
                            if (i < (length - 1)) {
                                if (s.charAt(i + 1) == '\n') {
                                    i++;
                                }
                            }
                        case '\n':
                            appendable.append("\r\n");
                            break;
                        default:
                            appendable.append(carac);
                    }
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            endQuote();
        }
        return columnNumber;
    }

    @Override
    public int addDateCell(FuzzyDate date) {
        if (!testCell()) {
            return columnNumber;
        }
        if (date != null) {
            try {
                appendable.append(date.toString());
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
        }
        return columnNumber;
    }

    @Override
    public int addMoneyCell(Amount amount) {
        if (!testCell()) {
            return columnNumber;
        }
        if (amount != null) {
            startQuote();
            try {
                appendable.append(amount.toLitteralString(symbols, false));
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            endQuote();
        }
        return columnNumber;
    }

    @Override
    public int addPercentageCell(Decimal decimal) {
        return addDecimalCell(decimal);
    }


    @Override
    public int endRow() {
        return rowNumber;
    }

    private void appendNewLine() {
        try {
            appendable.append("\r\n");
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    private boolean testCell() {
        columnNumber++;
        if (hiddenArray[columnNumber - 1]) {
            return false;
        } else {
            if (!firstCell) {
                try {
                    appendable.append(",");
                } catch (IOException ioe) {
                    throw new NestedIOException(ioe);
                }
            }
            firstCell = false;
            return true;
        }
    }

    private void startQuote() {
        try {
            appendable.append("\"");
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    private void endQuote() {
        try {
            appendable.append("\"");
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }


}
