// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/password_manager/core/browser/export/csv_writer.h"

#include "base/logging.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"

namespace {

// Encapsulates formatting and outputting values to a CSV file row by row. Takes
// care of escaping and adding the appropriate separators.
class CSVFormatter {
 public:
  explicit CSVFormatter(std::string* output)
      : output_(output), at_beginning_of_line_(true) {}

  // Escapes the |raw_value| if needed, adds a field separator unless we are at
  // the beginning of a line, and appends the escaped value to |output_|.
  void AppendValue(const std::string& raw_value);

  // Appends the platform-specific EOL terminator to |output_|.
  void EndLine();

 private:
  std::string* output_;
  bool at_beginning_of_line_;
};

void CSVFormatter::AppendValue(const std::string& raw_value) {
  // Append the field separator unless this is the first field on the line.
  if (!at_beginning_of_line_)
    output_->push_back(',');
  at_beginning_of_line_ = false;

  // Fields containing line breaks (CRLF), double quotes, and commas should be
  // enclosed in double-quotes. If double-quotes are used to enclose fields,
  // then double-quotes appearing inside a field must be escaped by preceding
  // them with another double quote.
  if (raw_value.find_first_of("\r\n\",") != std::string::npos) {
    output_->push_back('\"');
    output_->append(raw_value);
    base::ReplaceSubstringsAfterOffset(
        output_, output_->size() - raw_value.size(), "\"", "\"\"");
    output_->push_back('\"');
  } else {
    output_->append(raw_value);
  }
}

void CSVFormatter::EndLine() {
#if defined(OS_WIN)
  const char kLineEnding[] = "\r\n";
#else
  const char kLineEnding[] = "\n";
#endif
  output_->append(kLineEnding);
  at_beginning_of_line_ = true;
}

}  // namespace

namespace password_manager {

void WriteCSV(const std::vector<std::string>& column_names,
              const std::vector<std::map<std::string, std::string>>& records,
              std::string* csv) {
  DCHECK(csv);
  csv->clear();

  // Append header row.
  CSVFormatter formatter(csv);
  for (const auto& column_name : column_names) {
    formatter.AppendValue(column_name);
  }
  formatter.EndLine();

  // Append every other data record row.
  for (const auto& row : records) {
    for (const auto& column_name : column_names) {
      auto it_field = row.find(column_name);
      formatter.AppendValue(it_field != row.end() ?
          it_field->second : std::string());
    }
    formatter.EndLine();
  }
}

}  // namespace password_manager
