
package com.github.dmn1k.supercsv.io.declarative;

import com.github.dmn1k.supercsv.internal.cells.BeanCell;
import com.github.dmn1k.supercsv.internal.cells.BeanCells;
import com.github.dmn1k.supercsv.model.BeanDescriptor;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.exception.SuperCsvException;
import org.supercsv.exception.SuperCsvReflectionException;
import org.supercsv.io.AbstractCsvWriter;
import org.supercsv.prefs.CsvPreference;
import org.supercsv.util.Util;

/**
 * CsvDeclarativeBeanWriter writes a CSV file via conventions and {@link CellProcessorAnnotationDescriptor}
 * -annotations.
 *
 * @author Dominik Schlosser
 */
public class CsvDeclarativeBeanWriter extends AbstractCsvWriter {

    /**
     * Constructs a new <tt>CsvDeclarativeBeanWriter</tt> with the supplied Writer and CSV preferences. Note that the
     * <tt>writer</tt> will be wrapped in a <tt>BufferedWriter</tt> before accessed.
     *
     * @param writer the writer
     * @param preference the CSV preferences
     * @throws NullPointerException if writer or preference are null
     */
    public CsvDeclarativeBeanWriter(final Writer writer, final CsvPreference preference) {
        super(writer, preference);
    }

    /**
     * Writes a row of a CSV file, using the conventions and mappings provided
     * {@link CellProcessorAnnotationDescriptor}-annotations
     *
     * @param source The bean-instance to write
     * @throws IOException if an I/O error occurred
     * @throws IllegalArgumentException if source is null
     * @throws SuperCsvException if there was a general exception while writing/processing
     * @throws SuperCsvReflectionException if there was an reflection exception
     * @since 2.5
     */
    public void write(final Object source) throws IOException {
        if (source == null) {
            throw new IllegalArgumentException("source must not be null");
        }

        incrementRowAndLineNo();

        BeanDescriptor beanDescriptor = BeanDescriptor.create(source.getClass());
        BeanCells cells = BeanCells.getFields(beanDescriptor, StandardCsvContexts.WRITE);
        List<Object> beanValues = extractBeanValues(source, cells);

        List<Object> processedColumns = new ArrayList<>();

        List<CellProcessor> rowProcessors = new ArrayList<>();
        for (BeanCell cell : cells.getAll()) {
            rowProcessors.add(cell.getProcessor());
        }

        Util.executeCellProcessors(processedColumns, beanValues,
                rowProcessors.toArray(new CellProcessor[rowProcessors.size()]), getLineNumber(), getRowNumber());

        writeRow(processedColumns);
        flush();
    }

    private List<Object> extractBeanValues(final Object source, BeanCells cells) {

        List<Object> beanValues = new ArrayList<>();

        for (BeanCell cell : cells.getAll()) {
            beanValues.add(cell.getValue(source));
        }

        return beanValues;
    }
    
    @Override
    public void close() throws IOException {
        super.close();
        
        BeanCells.resetCache();
    }
}
