content: /*
   * CSVeed (https://github.com/42BV/CSVeed)
   *
   * Copyright 2013-2023 CSVeed.
   *
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of The Apache Software License,
   * Version 2.0 which accompanies this distribution, and is available at
   * https://www.apache.org/licenses/LICENSE-2.0.txt
   */
  package org.csveed.row;
  
  import java.io.IOException;
  import java.io.Writer;
  import java.util.Collection;
  import java.util.Iterator;
  
  import org.csveed.api.Header;
  import org.csveed.api.Row;
  import org.csveed.report.CsvException;
  import org.csveed.report.GeneralError;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * The Class RowWriterImpl.
   */
  public class RowWriterImpl implements RowWriter {
  
      /** The Constant LOG. */
      private static final Logger LOG = LoggerFactory.getLogger(RowWriterImpl.class);
  
      /** The writer. */
      private final Writer writer;
  
      /** The row instructions. */
      private RowInstructions rowInstructions;
  
      /** The header. */
      private Header header;
  
      /**
       * Instantiates a new row writer impl.
       *
       * @param writer
       *            the writer
       */
      public RowWriterImpl(Writer writer) {
          this(writer, new RowInstructionsImpl());
      }
  
      /**
       * Instantiates a new row writer impl.
       *
       * @param writer
       *            the writer
       * @param rowInstructions
       *            the row instructions
       */
      public RowWriterImpl(Writer writer, RowInstructions rowInstructions) {
          this.writer = writer;
          this.rowInstructions = rowInstructions;
      }
  
      @Override
      public void writeRows(String[][] rows) {
          for (String[] row : rows) {
              writeRow(row);
          }
      }
  
      @Override
      public void writeRows(Collection<Row> rows) {
          for (Row row : rows) {
              writeRow(row);
          }
      }
  
      @Override
      public Row writeRow(String[] cells) {
          Row row = new RowImpl(convertToLine(cells), header);
          writeRow(row);
          return row;
      }
  
      @Override
      public void writeRow(Row row) {
          if (rowInstructions.isUseHeader() && this.header == null) {
              throw new CsvException(
                      new GeneralError("Header has not been set for this table. Make sure to write it or configure "
                              + "it to be not used: .setUseHeader(false)"));
          }
          writeCells(row.iterator());
      }
  
      @Override
      public Header writeHeader(String[] headerNames) {
          Header header = new HeaderImpl(convertToLine(headerNames));
          writeHeader(header);
          return header;
      }
  
      @Override
      public void writeHeader(Header header) {
          this.header = header;
          writeCells(header.iterator());
      }
  
      @Override
      public RowInstructions getRowInstructions() {
          return this.rowInstructions;
      }
  
      /**
       * Write cells.
       *
       * @param cells
       *            the cells
       */
      private void writeCells(Iterator<String> cells) {
          int columnPosition = 1;
          try {
              while (cells.hasNext()) {
                  String cell = cells.next();
                  String nullSafeCell = cell != null ? cell : "";
                  String headerValue = header != null ? header.getName(columnPosition) : "";
                  LOG.debug("Writing cell value [{}] in column position [{}], header value is [{}].", nullSafeCell,
                          columnPosition, headerValue);
                  if (columnPosition != 1) {
                      writeSeparator();
                  }
                  if (rowInstructions.getQuotingEnabled()) {
                      writeQuotedCell(nullSafeCell);
                  } else {
                      writeCell(nullSafeCell);
                  }
                  columnPosition++;
              }
              writeEOL();
          } catch (IOException e) {
              LOG.trace("", e);
              throw new CsvException(new GeneralError("Error in writing to the writer: " + e.getMessage()));
          }
      }
  
      /**
       * Write EOL.
       *
       * @throws IOException
       *             Signals that an I/O exception has occurred.
       */
      private void writeEOL() throws IOException {
          writer.write(rowInstructions.getEndOfLine());
      }
  
      /**
       * Write separator.
       *
       * @throws IOException
       *             Signals that an I/O exception has occurred.
       */
      private void writeSeparator() throws IOException {
          writer.write(rowInstructions.getSeparator());
      }
  
      /**
       * Write quoted cell.
       *
       * @param cell
       *            the cell
       *
       * @throws IOException
       *             Signals that an I/O exception has occurred.
       */
      private void writeQuotedCell(String cell) throws IOException {
          writer.write(rowInstructions.getQuote());
          String searchString = Character.toString(rowInstructions.getQuote());
          String replaceString = new String(new char[] { rowInstructions.getEscape(), rowInstructions.getQuote() });
          String replacedString = cell.replace(searchString, replaceString);
          writeCell(replacedString);
          writer.write(rowInstructions.getQuote());
      }
  
      /**
       * Write cell.
       *
       * @param cell
       *            the cell
       *
       * @throws IOException
       *             Signals that an I/O exception has occurred.
       */
      private void writeCell(String cell) throws IOException {
          writer.write(cell);
      }
  
      /**
       * Convert to line.
       *
       * @param cells
       *            the cells
       *
       * @return the line with info
       */
      private LineWithInfo convertToLine(String[] cells) {
          LineWithInfo line = new LineWithInfo();
          for (String cell : cells) {
              line.addCell(cell);
          }
          return line;
      }
  
  }
  