/**
 * See https://github.com/eddelbuettel/rcppfastfloat/issues/4
 */

#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
#include <vector>

struct test_data {
  std::string input;
  bool expected_success;
  double expected_result;
};

bool eddelbuettel() {
  std::vector<test_data> const test_datas = {
      {"infinity", true, std::numeric_limits<double>::infinity()},
      {" \r\n\t\f\v3.16227766016838 \r\n\t\f\v", true, 3.16227766016838},
      {" \r\n\t\f\v3 \r\n\t\f\v", true, 3.0},
      {"  1970-01-01", false, 0.0},
      {"-NaN", true, std::numeric_limits<double>::quiet_NaN()},
      {"-inf", true, -std::numeric_limits<double>::infinity()},
      {" \r\n\t\f\v2.82842712474619 \r\n\t\f\v", true, 2.82842712474619},
      {"nan", true, std::numeric_limits<double>::quiet_NaN()},
      {" \r\n\t\f\v2.44948974278318 \r\n\t\f\v", true, 2.44948974278318},
      {"Inf", true, std::numeric_limits<double>::infinity()},
      {" \r\n\t\f\v2 \r\n\t\f\v", true, 2.0},
      {"-infinity", true, -std::numeric_limits<double>::infinity()},
      {" \r\n\t\f\v0 \r\n\t\f\v", true, 0.0},
      {" \r\n\t\f\v1.73205080756888 \r\n\t\f\v", true, 1.73205080756888},
      {" \r\n\t\f\v1 \r\n\t\f\v", true, 1.0},
      {" \r\n\t\f\v1.4142135623731 \r\n\t\f\v", true, 1.4142135623731},
      {" \r\n\t\f\v2.23606797749979 \r\n\t\f\v", true, 2.23606797749979},
      {"1970-01-02  ", false, 0.0},
      {" \r\n\t\f\v2.64575131106459 \r\n\t\f\v", true, 2.64575131106459},
      {"inf", true, std::numeric_limits<double>::infinity()},
      {"-nan", true, std::numeric_limits<double>::quiet_NaN()},
      {"NaN", true, std::numeric_limits<double>::quiet_NaN()},
      {"", false, 0.0},
      {"-Inf", true, -std::numeric_limits<double>::infinity()},
      {"+2.2", true, 2.2},
      {"1d+4", false, 0.0},
      {"1d-1", false, 0.0},
      {"0.", true, 0.0},
      {"-.1", true, -0.1},
      {"+.1", true, 0.1},
      {"1e+1", true, 10.0},
      {"+1e1", true, 10.0},
      {"-+0", false, 0.0},
      {"-+inf", false, 0.0},
      {"-+nan", false, 0.0},
  };
  for (size_t i = 0; i < test_datas.size(); i++) {
    auto const &input = test_datas[i].input;
    auto const expected_success = test_datas[i].expected_success;
    auto const expected_result = test_datas[i].expected_result;
    double result;
    // answer contains a error code and a pointer to the end of the
    // parsed region (on success).
    auto const answer = fast_float::from_chars(
        input.data(), input.data() + input.size(), result,
        fast_float::chars_format::general |
            fast_float::chars_format::allow_leading_plus |
            fast_float::chars_format::skip_white_space);
    if (answer.ec != std::errc()) {
      std::cout << "could not parse" << std::endl;
      if (expected_success) {
        return false;
      }
      continue;
    }
    bool non_space_trailing_content = false;
    if (answer.ptr != input.data() + input.size()) {
      // check that there is no content left
      for (char const *leftover = answer.ptr;
           leftover != input.data() + input.size(); leftover++) {
        if (!fast_float::is_space(*leftover)) {
          non_space_trailing_content = true;
          break;
        }
      }
    }
    if (non_space_trailing_content) {
      std::cout << "found trailing content " << std::endl;
      if (!expected_success) {
        continue;
      } else {
        return false;
      }
    }
    std::cout << "parsed " << result << std::endl;
    if (!expected_success) {
      return false;
    }
    if (result != expected_result &&
        !(std::isnan(result) && std::isnan(expected_result))) {
      std::cout << "results do not match. Expected " << expected_result
                << std::endl;
      return false;
    }
  }
  return true;
}

int main() {
  if (!eddelbuettel()) {
    printf("Bug.\n");
    return EXIT_FAILURE;
  }
  printf("All ok.\n");
  return EXIT_SUCCESS;
}
