#pragma once
// undo these tokens to use numeric_limits below
#undef min
#undef max

// Include platform.h first to ensure Windows types are defined
#include <platform.h>

// Then include ODBC headers which need the Windows types
#include <common/odbc_common.h>

// Standard library includes
#include <limits>
#include <vector>
#include <string.h>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <string>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <Logger.h>
#include <algorithm>

#ifdef LINUX_BUILD
#include <cmath>
#include <cfloat>
#endif

namespace mssql {
using namespace std;

class DatumStorageLegacy {
 public:
  typedef long long int bigint_t;
  typedef vector<uint16_t> uint16_t_vec_t;
  typedef vector<shared_ptr<uint16_t_vec_t>> uint16_vec_t_vec_t;
  typedef vector<char> char_vec_t;
  typedef vector<shared_ptr<char_vec_t>> char_vec_t_vec_t;
  typedef vector<int8_t> int8_vec_t;
  typedef vector<int16_t> int16_vec_t;
  typedef vector<int32_t> int32_vec_t;
  typedef vector<uint32_t> uint32_vec_t;
  typedef vector<int64_t> int64_vec_t;
  typedef vector<double> double_vec_t;
  typedef vector<bigint_t> bigint_vec_t;
  typedef vector<SQL_SS_TIMESTAMPOFFSET_STRUCT> timestamp_offset_vec_t;
  typedef vector<SQL_SS_TIME2_STRUCT> time2_struct_vec_t;
  typedef vector<SQL_TIMESTAMP_STRUCT> timestamp_struct_vec_t;
  typedef vector<SQL_DATE_STRUCT> date_struct_vec_t;
  typedef vector<SQL_NUMERIC_STRUCT> numeric_struct_vec_t;

  DatumStorageLegacy()
      : int8vec_ptr(nullptr),
        int16vec_ptr(nullptr),
        int32vec_ptr(nullptr),
        uint32vec_ptr(nullptr),
        int64vec_ptr(nullptr),
        doublevec_ptr(nullptr),
        timestampoffsetvec_ptr(nullptr),
        time2vec_ptr(nullptr),
        timestampvec_ptr(nullptr),
        datevec_ptr(nullptr),
        numeric_ptr(nullptr),
        charvec_ptr(nullptr),
        uint16vec_ptr(nullptr),
        uint16_vec_vec_ptr(nullptr),
        char_vec_vec_ptr(nullptr),
        bigint_vec_ptr(nullptr) {}

  template <typename T>
  inline shared_ptr<vector<T>> reserve_vec(shared_ptr<vector<T>> existing, size_t size) {
    if (existing == nullptr) {
      existing = make_shared<vector<T>>(size);
    } else {
      if (size > existing->capacity()) {
        existing->reserve(size);
      }
    }
    return existing;
  }

  inline bool isNumeric() const {
    return numeric_ptr && !numeric_ptr->empty();
  }

  inline void ReserveNumerics(size_t len) {
    numeric_ptr = reserve_vec<SQL_NUMERIC_STRUCT>(numeric_ptr, len);
  }

  inline bool isChar() const {
    return charvec_ptr && !charvec_ptr->empty();
  }

  inline void ReserveChars(size_t len) {
    charvec_ptr = reserve_vec<char>(charvec_ptr, len);
  }

  inline bool isBigInt() const {
    return bigint_vec_ptr && !bigint_vec_ptr->empty();
  }

  inline void ReserveBigInt(size_t len) {
    bigint_vec_ptr = reserve_vec<bigint_t>(bigint_vec_ptr, len);
  }

  inline void ReserveUint16(size_t len) {
    uint16vec_ptr = reserve_vec<uint16_t>(uint16vec_ptr, len);
  }

  inline bool isUint16Vec() const {
    return uint16_vec_vec_ptr && !uint16_vec_vec_ptr->empty();
  }

  inline void ReserveUint16Vec(size_t len) {
    uint16_vec_vec_ptr = reserve_vec<shared_ptr<uint16_t_vec_t>>(uint16_vec_vec_ptr, len);
  }

  inline bool isInt32() const {
    return int32vec_ptr && !int32vec_ptr->empty();
  }

  inline void ReserveInt32(size_t len) {
    int32vec_ptr = reserve_vec<int32_t>(int32vec_ptr, len);
  }

  inline bool isInt8() const {
    return int8vec_ptr && !int8vec_ptr->empty();
  }

  inline void ReserveInt8(size_t len) {
    int8vec_ptr = reserve_vec<int8_t>(int8vec_ptr, len);
  }

  inline bool isInt16() const {
    return int16vec_ptr && !int16vec_ptr->empty();
  }

  inline void ReserveInt16(size_t len) {
    int16vec_ptr = reserve_vec<int16_t>(int16vec_ptr, len);
  }

  inline bool isUInt32() const {
    return uint32vec_ptr && !uint32vec_ptr->empty();
  }

  inline void ReserveUInt32(size_t len) {
    uint32vec_ptr = make_shared<uint32_vec_t>(len);
  }

  inline bool isInt64() const {
    return int64vec_ptr && !int64vec_ptr->empty();
  }

  inline void ReserveInt64(size_t len) {
    int64vec_ptr = reserve_vec<int64_t>(int64vec_ptr, len);
  }

  inline bool isDouble() const {
    return doublevec_ptr && !doublevec_ptr->empty();
  }

  inline void ReserveDouble(size_t len) {
    doublevec_ptr = reserve_vec<double>(doublevec_ptr, len);
  }

  inline bool isTimestamp() const {
    return timestampvec_ptr && !timestampvec_ptr->empty();
  }

  inline void ReserveTimestamp(size_t len) {
    timestampvec_ptr = reserve_vec<SQL_TIMESTAMP_STRUCT>(timestampvec_ptr, len);
  }

  inline bool isTime2() const {
    return time2vec_ptr && !time2vec_ptr->empty();
  }

  inline void Reservetime2(size_t len) {
    time2vec_ptr = reserve_vec<SQL_SS_TIME2_STRUCT>(time2vec_ptr, len);
  }

  inline bool isTimestampOffset() const {
    return timestampoffsetvec_ptr && !timestampoffsetvec_ptr->empty();
  }

  inline void ReserveTimestampOffset(size_t len) {
    timestampoffsetvec_ptr =
        reserve_vec<SQL_SS_TIMESTAMPOFFSET_STRUCT>(timestampoffsetvec_ptr, len);
  }

  inline bool isCharVec() const {
    return char_vec_vec_ptr && !char_vec_vec_ptr->empty();
  }

  inline void ReserveCharVec(size_t len) {
    char_vec_vec_ptr = reserve_vec<shared_ptr<char_vec_t>>(char_vec_vec_ptr, len);
  }

  inline bool isDate() const {
    return datevec_ptr && !datevec_ptr->empty();
  }

  inline void ReserveDate(size_t len) {
    datevec_ptr = reserve_vec<SQL_DATE_STRUCT>(datevec_ptr, len);
  }

  shared_ptr<int8_vec_t> int8vec_ptr;
  shared_ptr<int16_vec_t> int16vec_ptr;
  shared_ptr<int32_vec_t> int32vec_ptr;
  shared_ptr<uint32_vec_t> uint32vec_ptr;
  shared_ptr<int64_vec_t> int64vec_ptr;
  shared_ptr<double_vec_t> doublevec_ptr;
  shared_ptr<timestamp_offset_vec_t> timestampoffsetvec_ptr;
  shared_ptr<time2_struct_vec_t> time2vec_ptr;
  shared_ptr<timestamp_struct_vec_t> timestampvec_ptr;
  shared_ptr<date_struct_vec_t> datevec_ptr;
  shared_ptr<numeric_struct_vec_t> numeric_ptr;
  shared_ptr<char_vec_t> charvec_ptr;
  shared_ptr<uint16_t_vec_t> uint16vec_ptr;
  shared_ptr<uint16_vec_t_vec_t> uint16_vec_vec_ptr;
  shared_ptr<char_vec_t_vec_t> char_vec_vec_ptr;
  shared_ptr<bigint_vec_t> bigint_vec_ptr;

  wstring schema;
  wstring table;

 private:
};

class nodeTypeCounter {
 public:
  void Decode(const Napi::Object& p) {
    if (p.IsNull() || p.IsUndefined()) {
      ++nullCount;
    } else if (p.IsString()) {
      ++stringCount;
    } else if (p.IsBoolean()) {
      ++boolCount;
    } else if (p.IsNumber()) {
      const auto d = p.ToNumber().DoubleValue();
#ifdef WINDOWS_BUILD
      if (_isnan(d))
        ++nanCount;
#endif
#ifdef LINUX_BUILD
      if (isnan(d))
        ++nanCount;
#endif
#ifdef WINDOWS_BUILD
      else if (!_finite(d))
        ++infiniteCount;
#endif
#ifdef LINUX_BUILD
      else if (!isfinite(d))
        ++infiniteCount;
#endif

      if (d == floor(d)) {
        // Check if it's an integer
        if (d >= std::numeric_limits<int32_t>::min() && d <= std::numeric_limits<int32_t>::max()) {
          ++int32Count;
        } else if (d >= 0 && d <= std::numeric_limits<uint32_t>::max()) {
          ++uint32Count;
        } else if (d >= std::numeric_limits<int64_t>::min() && d <= std::numeric_limits<int64_t>::max()) {
          ++int64Count;
        } else {
          ++numberCount;
        }
      } else {
        ++numberCount;
      }
    } /*
     else if (p->IsBigInt())
     {
       MaybeLocal<BigInt> maybe = p->ToBigInt(context);
       Local<BigInt> local;
       if (maybe.ToLocal(&local))
       {
         ++int64Count;
       }
     }*/
    else if (p.IsDate()) {
      ++dateCount;
    } else if (p.IsBuffer()) {
      ++bufferCount;
    } else {
      ++invalidCount;
    }
  }

  int getoutBoundsCount() const {
    return nanCount + infiniteCount;
  }

  int boolCount = 0;
  int stringCount = 0;
  int nullCount = 0;
  int int32Count = 0;
  int uint32Count = 0;
  int numberCount = 0;
  int dateCount = 0;
  int bufferCount = 0;
  int invalidCount = 0;
  int nanCount = 0;
  int infiniteCount = 0;
  int int64Count = 0;
};
}  // namespace mssql