void Cask::CdapOdbc::DataReader::fetchValue(const web::json::value& value, const ColumnBinding& binding) {
  std::wstring strValue;
  SQLDOUBLE dblValue = NAN;
  switch (binding.getTargetType()) {
    case SQL_CHAR:
      strValue = to_wstring(value);
      this->fetchVarchar(strValue.c_str(), binding);
      break;
    case SQL_WCHAR:
      strValue = to_wstring(value);
      this->fetchWVarchar(strValue.c_str(), binding);
      break;
    case SQL_DOUBLE:
      if (value.is_string()) {
        dblValue = std::wcstod(value.as_string().c_str(), nullptr);
      } else if (value.is_integer()) {
        dblValue = value.as_integer();
      } else if (value.is_double()) {
        dblValue = value.as_double();
      } else if (value.is_number()) {
        dblValue = value.as_number().to_double();
      } else if (value.is_boolean()) {
        dblValue = value.as_bool() ? 1.0 : 0.0;
      }

      this->fetchDouble(dblValue, binding);
      break;
  }
}
void Cask::CdapOdbc::ColumnsDataReader::getColumnValue(const ColumnBinding& binding) {
  auto& record = this->queryResult.getRows().at(this->currentRowIndex);
  std::wstring name;
  std::wstring typeName;
  SQLSMALLINT radix = 0;

  switch (binding.getColumnNumber()) {
    case 1: // TABLE_CAT 
    case 2: // TABLE_SCHEM 
    case 9: // DECIMAL_DIGITS 
    case 12: // REMARKS 
    case 13: // COLUMN_DEF 
    case 15: // SQL_DATETIME_SUB 
    case 16: // CHAR_OCTET_LENGTH 
      this->fetchNull(binding);
      break;
    case 3: // TABLE_NAME 
      this->fetchVarchar(this->tableName.c_str(), binding);
      break;
    case 4: // COLUMN_NAME 
      name = record.at(L"name").as_string();
      this->fetchVarchar(name.c_str(), binding);
      break;
    case 5: // DATA_TYPE
    case 14: // SQL_DATA_TYPE 
      this->fetchSmallint(getDataType(record.at(L"type")), binding);
      break;
    case 6: // TYPE_NAME 
      typeName = getTypeName(record.at(L"type"));
      this->fetchVarchar(typeName.c_str(), binding);
      break;
    case 7: // COLUMN_SIZE 
      this->fetchInt(getColumnSize(record.at(L"type")), binding);
      break;
    case 8: // BUFFER_LENGTH 
      this->fetchInt(getBufferLength(record.at(L"type")), binding);
      break;
    case 10: // NUM_PREC_RADIX 
      radix = getRadix(record.at(L"type"));
      if (radix > 0) {
        this->fetchSmallint(radix, binding);
      } else {
        this->fetchNull(binding);
      }

      break;
    case 11: // NULLABLE
      this->fetchSmallint(getIsNull(record.at(L"type")), binding);
      break;
    case 17: // ORDINAL_POSITION 
      this->fetchInt(this->currentRowIndex + 1, binding);
      break;
    case 18: // IS_NULLABLE 
      this->fetchVarchar((getIsNull(record.at(L"type")) == SQL_NO_NULLS) ? L"NO" : L"YES", binding);
      break;
  }
}
void Cask::CdapOdbc::QueryDataReader::getColumnValue(const ColumnBinding& binding) {
  auto& row = this->queryResult.getRows().at(this->currentRowIndex);
  auto& value = row.at(L"columns").as_array().at(binding.getColumnNumber() - 1);
  
  if (value.is_null()) {
    this->fetchNull(binding);
  } else {
    this->fetchValue(value, binding);
  }
}
void Cask::CdapOdbc::TypesDataReader::getColumnValue(const ColumnBinding& binding) {
    switch (binding.getColumnNumber()) {
    case 1: // TYPE_NAME
        this->fetchVarchar(L"string", binding);
        break;
    case 2: // DATA_TYPE
    case 16: // SQL_DATA_TYPE
        this->fetchSmallint(SQL_VARCHAR, binding);
        break;
    case 3: // COLUMN_SIZE
        this->fetchSmallint(2000, binding);
        break;
    case 4: // LITERAL_PREFIX
    case 5: // LITERAL_SUFFIX
    case 10: // UNSIGNED_ATTRIBUTE
    case 12: // AUTO_UNIQUE_VALUE
    case 13: // LOCAL_TYPE_NAME
    case 14: // MINIMUM_SCALE
    case 15: // MAXIMUM_SCALE
    case 17: // SQL_DATETIME_SUB
    case 18: // NUM_PREC_RADIX
    case 19: // INTERVAL_PRECISION
        this->fetchNull(binding);
        break;
    case 6: // CREATE_PARAMS
        this->fetchVarchar(L"length", binding);
        break;
    case 7: // NULLABLE
        this->fetchSmallint(SQL_NULLABLE, binding);
        break;
    case 8: // CASE_SENSITIVE
        this->fetchSmallint(SQL_TRUE, binding);
        break;
    case 9: // SEARCHABLE
        this->fetchSmallint(SQL_SEARCHABLE, binding);
        break;
    case 11: // FIXED_PREC_SCALE
        this->fetchSmallint(SQL_FALSE, binding);
        break;
    }
}
void Cask::CdapOdbc::DataReader::fetchWVarchar(const wchar_t* str, const ColumnBinding& binding) {
  assert(binding.getTargetType() == SQL_WCHAR || binding.getTargetType() == SQL_DEFAULT);
  if (str) {
    std::wstring uniStr = str;
    size_t maxLength = static_cast<size_t>(binding.getBufferLength()) - 1;
    size_t size = (uniStr.size() < maxLength) ? uniStr.size() : maxLength;
    if (binding.getTargetValuePtr()) {
      wchar_t* outString = static_cast<wchar_t*>(binding.getTargetValuePtr());
      auto it = stdext::make_checked_array_iterator<wchar_t*>(outString, size);
      std::copy(uniStr.begin(), uniStr.begin() + size, it);
      outString[size] = 0;
    }

    if (binding.getStrLenOrInd()) {
      if (size <= maxLength) {
        *binding.getStrLenOrInd() = size;
      } else {
        *binding.getStrLenOrInd() = SQL_NO_TOTAL;
      }
    }
  } else {
    if (binding.getTargetValuePtr() && binding.getBufferLength() > 0) {
      wchar_t* outString = static_cast<wchar_t*>(binding.getTargetValuePtr());
      outString[0] = 0;
    }

    if (binding.getStrLenOrInd()) {
      *binding.getStrLenOrInd() = SQL_NULL_DATA;
    }
  }
}
void Cask::CdapOdbc::DataReader::fetchNull(const ColumnBinding& binding) {
  if (binding.getStrLenOrInd()) {
    *binding.getStrLenOrInd() = SQL_NULL_DATA;
  }
}
void Cask::CdapOdbc::DataReader::fetchInt(SQLINTEGER value, const ColumnBinding& binding) {
  assert(binding.getTargetType() == SQL_C_SLONG || binding.getTargetType() == SQL_DEFAULT);
  *(reinterpret_cast<SQLINTEGER*>(binding.getTargetValuePtr())) = value;
}
void Cask::CdapOdbc::DataReader::fetchDouble(SQLDOUBLE value, const ColumnBinding& binding) {
  assert(binding.getTargetType() == SQL_DOUBLE || binding.getTargetType() == SQL_DEFAULT);
  *(reinterpret_cast<SQLDOUBLE*>(binding.getTargetValuePtr())) = value;
}
void Cask::CdapOdbc::DataReader::fetchSmallint(SQLSMALLINT value, const ColumnBinding& binding) {
  assert(binding.getTargetType() == SQL_C_SSHORT || binding.getTargetType() == SQL_DEFAULT);
  *(reinterpret_cast<SQLSMALLINT*>(binding.getTargetValuePtr())) = value;
}