// Helpers for the top module

#include "pg_prelude.h"

#include "aggs.h"

const char NEWLINE = '\n';
const char DQUOTE  = '"';
const char CR      = '\r';
const char BOM[]   = {0xEF, 0xBB, 0xBF};

static inline bool is_reserved(char c) {
  return c == DQUOTE || c == NEWLINE || c == CR;
}

// Any comma, quote, CR, LF requires quoting as per RFC https://www.ietf.org/rfc/rfc4180.txt
static inline bool needs_quote(const char *s, size_t n, char delim) {
  while (n--) {
    char c = *s++;
    if (c == delim || is_reserved(c)) return true;
  }
  return false;
}

void parse_csv_options(HeapTupleHeader opts_hdr, CsvOptions *csv_opts) {
  // defaults
  csv_opts->delimiter = ',';
  csv_opts->bom       = false;
  csv_opts->header    = true;
  csv_opts->nullstr   = NULL;

  if (opts_hdr == NULL) return;

  TupleDesc desc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(opts_hdr),
                                          HeapTupleHeaderGetTypMod(opts_hdr));

  Datum values[csv_options_count];
  bool  nulls[csv_options_count];

  heap_deform_tuple(
      &(HeapTupleData){.t_len = HeapTupleHeaderGetDatumLength(opts_hdr), .t_data = opts_hdr}, desc,
      values, nulls);

  if (!nulls[0]) {
    csv_opts->delimiter = DatumGetChar(values[0]);
    if (is_reserved(csv_opts->delimiter))
      ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                      errmsg("delimiter cannot be newline, carriage return or "
                             "double quote")));
  }

  if (!nulls[1]) {
    csv_opts->bom = DatumGetBool(values[1]);
  }

  if (!nulls[2]) {
    csv_opts->header = DatumGetBool(values[2]);
  }

  if (!nulls[3]) {
    csv_opts->nullstr = DatumGetTextPP(values[3]);
  }

  ReleaseTupleDesc(desc);
}

void csv_append_field(StringInfo buf, const char *s, size_t n, char delim) {
  if (!needs_quote(s, n, delim)) {
    appendBinaryStringInfo(buf, s, n);
  } else {
    appendStringInfoChar(buf, DQUOTE);
    for (size_t j = 0; j < n; j++) {
      char c = s[j];
      if (c == DQUOTE) appendStringInfoChar(buf, DQUOTE);
      appendStringInfoChar(buf, c);
    }
    appendStringInfoChar(buf, DQUOTE);
  }
}
