/*
 * This file is a part of the CaosDB Project.
 *
 * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
 * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "caosdb/exceptions.h"
#include "maoxdb.hpp"
#include "mex.h"
// #include "mexproto.h"
#include <gtest/gtest.h>
#include <limits>
#include <string>
#include <vector>

namespace maoxdb {

///////////////////////////////////////////////////////////////////////////////
//                              Helper functions                             //
///////////////////////////////////////////////////////////////////////////////

/*
 * These functions test if the value can be converted to a mex scalar.
 */
void test_scalar_uint64(UINT64_T value) {
  mxArray *scalar = mxScalarUINT64(value);
  EXPECT_TRUE(mxIsUint64(scalar));
  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
  EXPECT_EQ(*((UINT64_T *) mxGetData(scalar)), value);
}

void test_scalar_int64(INT64_T value) {
  mxArray *scalar = mxScalarINT64(value);
  EXPECT_TRUE(mxIsInt64(scalar));
  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
  EXPECT_EQ(*((INT64_T *) mxGetData(scalar)), value);
}

void test_scalar_double(double value) {
  mxArray *scalar = mxScalarDOUBLE(value);
  EXPECT_TRUE(mxIsDouble(scalar));
  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
  EXPECT_EQ(*((double *) mxGetData(scalar)), value);
}

void test_scalar_logical(bool value) {
  mxArray *scalar = mxScalarLOGICAL(value);
  EXPECT_TRUE(mxIsLogical(scalar));
  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
  EXPECT_EQ(*((bool *) mxGetData(scalar)), value);
}

/**
 * Generate a vector with useful values for testing.
 */
template<typename T>
auto values() -> std::vector<T> {
  std::vector<T> values =
    {0, 1,
     std::numeric_limits<T>::max(),
     std::numeric_limits<T>::min(),
     std::numeric_limits<T>::lowest(),
     std::numeric_limits<T>::epsilon() // 0 for integers, but who cares?
    };
  return values;
}

///////////////////////////////////////////////////////////////////////////////
//                                Actual tests                               //
///////////////////////////////////////////////////////////////////////////////

/**
 * Test if construction of scalar mex arrays works
 */
TEST(test_utilities, scalar_arrays) {
  auto values_uint64 = values<UINT64_T>();
  for (auto value : values_uint64) {
    test_scalar_uint64(value);
  }

  auto values_int64 = values<INT64_T>();
  for (auto value : values_int64) {
    test_scalar_int64(value);
  }

  auto values_double = values<double>();
  for (auto value : values_double) {
    test_scalar_double(value);
  }

  std::vector<bool> values_logical = {true, false};
  for (auto value : values_logical) {
    test_scalar_logical(value);
  }
}

/**
 * Test exception handling
 */

TEST(test_utilities, exception_handling) {
  caosdb::exceptions::AuthenticationError exc("Authentication failed.");
  auto strings = exceptionToMessage(exc);
  EXPECT_EQ(strings.first, std::string("16"));
  EXPECT_EQ(strings.second,
            std::string("The attempt to execute this transaction has not been "
                        "executed at all because the authentication did not "
                        "succeed.\nAuthentication failed."));
}

}  // maoxdb