/* FichothequeLib_Tools - Copyright (c) 2015-2026 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.time.format.FormatStyle;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.exportation.table.ColDef;
import net.fichotheque.exportation.table.ColDefTitleProvider;
import net.fichotheque.exportation.table.SubsetTable;
import net.fichotheque.exportation.table.SumCellConverter;
import net.fichotheque.exportation.table.SumResult;
import net.fichotheque.exportation.table.TableExportConstants;
import net.fichotheque.namespaces.FormatSpace;
import net.fichotheque.utils.TableDefUtils;
import net.mapeadores.opendocument.io.odtable.OdsXMLPart;
import net.mapeadores.opendocument.io.odtable.StyleManager;
import net.mapeadores.util.date.DateConstants;
import net.mapeadores.util.date.FuzzyDate;
import net.mapeadores.util.date.FuzzyDateFormatter;
import net.mapeadores.util.exceptions.NestedIOException;
import net.mapeadores.util.money.Amount;
import net.mapeadores.util.primitives.Decimal;
import net.mapeadores.util.table.TableWriter;
import net.mapeadores.util.xml.XMLWriter;


/**
 *
 * @author Vincent Calame
 */
public class SubsetTableOdsXMLPart extends OdsXMLPart {

    private final static String COLUMNHEADER_STYLE = "ColumnHeader";
    private final static String TITLE_STYLE = "Title";
    private final TableExportOdsParameters tableExportOdsParameters;


    public SubsetTableOdsXMLPart(XMLWriter xmlWriter, StyleManager styleManager, TableExportOdsParameters tableExportOdsParameters) {
        super(xmlWriter, styleManager);
        this.tableExportOdsParameters = tableExportOdsParameters;
    }

    public int addSubsetTable(String tableName, SubsetTable subsetTable, Collection<SubsetItem> subsetItems, int startRowNumber, NamedRangeHandler namedRangeHandler) throws IOException {
        Subset subset = subsetTable.getSubset();
        int rowCount = 0;
        ColDefTitleProvider colDefTitleProvider = getColDefTitleProvider(subset);
        if (colDefTitleProvider != null) {
            printHeader(subsetTable.getColDefList(), colDefTitleProvider);
            startRowNumber++;
            rowCount++;
        }
        PatternChecker patternChecker = buildPatternChecker(subsetTable.getColDefList());
        InternalTableWriter tableWriter = new InternalTableWriter(tableName, startRowNumber, new FormulaEngine(subsetTable), patternChecker);
        try {
            SumCellConverter sumCellConverter = tableExportOdsParameters.getSumCellConverter();
            TableExportEngine.exportSubset(subsetTable, tableWriter, tableExportOdsParameters.getSumCellConverter(), subsetItems);
            SumResult sumResult = sumCellConverter.getSumResult(subsetTable);
            rowCount = rowCount + tableWriter.rowCount;
            namedRangeHandler.setCurrentSubset(subset.getSubsetKey());
            rowCount = rowCount + TableExportEngine.writeSumRows(tableWriter, sumResult, tableWriter.rowNumber + 1, namedRangeHandler);
        } catch (NestedIOException nioe) {
            throw nioe.getIOException();
        }
        return rowCount;
    }

    public int addTitleRows(String title) throws IOException {
        this
                .rowStart()
                .stringCell(title, TITLE_STYLE)
                .rowEnd()
                .rowStart()
                .rowEnd();
        return 2;
    }


    private void printHeader(List<ColDef> colDefList, ColDefTitleProvider colDefTitleProvider) throws IOException {
        rowStart();
        for (ColDef colDef : colDefList) {
            stringCell(colDefTitleProvider.getTitle(colDef), COLUMNHEADER_STYLE, 1);
        }
        rowEnd();
    }

    private PatternChecker buildPatternChecker(List<ColDef> colDefList) {
        PatternChecker patternChecker = new PatternChecker();
        int columnNumber = 1;
        for (ColDef colDef : colDefList) {
            String dateStyle = colDef.getAttributes().getFirstValue(FormatSpace.DATESTYLE_KEY);
            if (dateStyle != null) {
                FormatStyle formatStyle = DateConstants.getMatchingFormatStyle(dateStyle);
                if (formatStyle != null) {
                    FuzzyDateFormatter fuzzyDateFormatter = FuzzyDateFormatter.build(formatStyle, tableExportOdsParameters.getUserLangContext().getFormatLocale());
                    patternChecker.add(columnNumber, fuzzyDateFormatter);
                }
            }
            columnNumber++;
        }
        return patternChecker;
    }

    private ColDefTitleProvider getColDefTitleProvider(Subset subset) {
        switch (tableExportOdsParameters.getHeaderType()) {
            case TableExportConstants.COLUMNTITLE_HEADER:
                return TableDefUtils.getLabelColDefTitleProvider(tableExportOdsParameters.getWorkingLang(), tableExportOdsParameters.getTableExportContext().getSourceLabelProvider(), subset);
            case TableExportConstants.COLUMNKEY_HEADER:
                return TableDefUtils.KEY_COLDEFTITLEPROVIDER;
            default:
                return null;
        }
    }


    private class PatternChecker {

        private final Map<Integer, FuzzyDateFormatter> patternMap = new HashMap<Integer, FuzzyDateFormatter>();

        private String getPattern(String fuzzyDateType, int columnNumber) {
            FuzzyDateFormatter fuzzyDateFormatter = patternMap.get(columnNumber);
            if (fuzzyDateFormatter == null) {
                return null;
            }
            return fuzzyDateFormatter.getPattern(fuzzyDateType);
        }

        private void add(int columnNumber, FuzzyDateFormatter fuzzyDateFormatter) {
            patternMap.put(columnNumber, fuzzyDateFormatter);
        }

    }


    private class InternalTableWriter implements TableWriter {

        private final String tableName;
        private final FormulaEngine formulaEngine;
        private final PatternChecker patternChecker;
        private int rowNumber;
        private int rowCount;
        private int columnNumber;


        private InternalTableWriter(String tableName, int startRowNumber, FormulaEngine formulaEngine, PatternChecker patternChecker) {
            this.tableName = tableName;
            this.rowNumber = startRowNumber - 1;
            this.rowCount = 0;
            this.formulaEngine = formulaEngine;
            this.patternChecker = patternChecker;
        }

        @Override
        public int startRow() {
            rowNumber++;
            rowCount++;
            columnNumber = 0;
            try {
                rowStart();
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return rowNumber;
        }

        @Override
        public int addIntegerCell(Long lg) {
            columnNumber++;
            try {
                if (lg == null) {
                    emptyCell();
                } else {
                    numberCell(lg.toString());
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addDecimalCell(Decimal decimal) {
            columnNumber++;
            try {
                if (decimal == null) {
                    emptyCell();
                } else {
                    numberCell(decimal.toString());
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addDateCell(FuzzyDate date) {
            columnNumber++;
            try {
                if (date == null) {
                    emptyCell();
                } else {
                    String cellStyleName = getStyleManager().getDateCellStyleName(tableName, columnNumber, patternChecker.getPattern(date.getDateType(), columnNumber));
                    dateCell(date, cellStyleName);
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addStringCell(String s) {
            columnNumber++;
            try {
                if (s == null) {
                    emptyCell();
                } else {
                    if (formulaEngine.isFormulaColumn(columnNumber)) {
                        formulaEngine.writeFormulaCell(rowNumber, columnNumber, SubsetTableOdsXMLPart.this, s);
                    } else {
                        stringCell(s);
                    }
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addMoneyCell(Amount amount) {
            columnNumber++;
            try {
                if (amount == null) {
                    emptyCell();
                } else {
                    String cellStyleName = getStyleManager().getCurrencyCellStyleName(tableName, columnNumber, amount.getCurrency());
                    currencyCell(amount, cellStyleName);
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addPercentageCell(Decimal decimal) {
            columnNumber++;
            try {
                if (decimal == null) {
                    emptyCell();
                } else {
                    percentageCell(decimal.toString());
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int endRow() {
            try {
                rowEnd();
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return rowNumber;
        }

    }

}
