///////////////////////////////////////////////////////////////////////////////
// BSD 3-Clause License
//
// Copyright (C) 2020 LAAS-CNRS
// Copyright note valid unless otherwise stated in individual files.
// All rights reserved.
///////////////////////////////////////////////////////////////////////////////

// modified from
// https://gist.github.com/rudolfovich/f250900f1a833e715260a66c87369d15

#ifndef CROCODDYL_CORE_UTILS_FILE_IO_HPP_
#define CROCODDYL_CORE_UTILS_FILE_IO_HPP_

#include <fstream>
#include <sstream>
#include <string>

class CsvStream {
  std::ofstream fs_;
  bool is_first_;
  const std::string separator_;
  const std::string escape_seq_;
  const std::string special_chars_;

 public:
  CsvStream(const std::string filename, const std::string separator = ",")
      : fs_(),
        is_first_(true),
        separator_(separator),
        escape_seq_("\""),
        special_chars_("\"") {
    fs_.exceptions(std::ios::failbit | std::ios::badbit);
    fs_.open(filename.c_str());
  }

  ~CsvStream() {
    flush();
    fs_.close();
  }

  void flush() { fs_.flush(); }

  inline static CsvStream& endl(CsvStream& file) {
    file.endrow();
    return file;
  }

  void endrow() {
    fs_ << std::endl;
    is_first_ = true;
  }

  CsvStream& operator<<(CsvStream& (*val)(CsvStream&)) { return val(*this); }

  CsvStream& operator<<(const char* val) { return write(escape(val)); }

  CsvStream& operator<<(const std::string& val) { return write(escape(val)); }

  template <typename T>
  CsvStream& operator<<(const T& val) {
    return write(val);
  }

 private:
  template <typename T>
  CsvStream& write(const T& val) {
    if (!is_first_) {
      fs_ << separator_;
    } else {
      is_first_ = false;
    }
    fs_ << val;
    return *this;
  }

  std::string escape(const std::string& val) {
    std::ostringstream result;
    result << '"';
    std::string::size_type to, from = 0u, len = val.length();
    while (from < len && std::string::npos !=
                             (to = val.find_first_of(special_chars_, from))) {
      result << val.substr(from, to - from) << escape_seq_ << val[to];
      from = to + 1;
    }
    result << val.substr(from) << '"';
    return result.str();
  }
};

#endif  // CROCODDYL_CORE_UTILS_FILE_IO_HPP_
