/*!
 * \file TmetaBasic.cpp
 *
 * Copyright (C) 2018 Christos Choutouridis
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include <utl/meta/meta.h>
#include <gtest/gtest.h>
#include <type_traits>

namespace TmetaBasic {
   using namespace utl;
   using namespace meta;

   /*
    * Types to behave like Fixtures
    */
   // Test type_of fixture
   template<class T> struct Identity {
      using type = T;
   };

   /*
    * Test integral constant
    */
   TEST(TmetaBasic, IntegrealType) {
      EXPECT_EQ(true, (std::is_same<int, eval<Identity<int>>>::value));
      EXPECT_EQ(true, (std::is_same<nil_, eval<nil_>>::value));
      EXPECT_EQ(true, (std::is_same<nil_, eval<eval<nil_>>>::value));
      EXPECT_EQ(true, (std::is_same<nil_, eval<eval<eval<nil_>>>>::value));
   }
   TEST(TmetaBasic, IntegrealConstant) {
      EXPECT_EQ(true, (std::is_same<int, integral_<int, 42>::value_type>::value));
      EXPECT_EQ(true, (std::is_same<int, integral_<int, 42>::type::value_type>::value));
      EXPECT_EQ(42, (integral_<int, 0>::value_type(42)));
      EXPECT_EQ(42, (integral_<int, 42>()));
   }
   TEST(TmetaBasic, BasicTypes) {
      EXPECT_EQ(true, (std::is_same<bool, bool_<false>::value_type>::value));
      EXPECT_EQ(true, bool_<true>::value);
      EXPECT_EQ(true, (std::is_same<bool, false_::value_type>::value));
      EXPECT_EQ(false, false_::value);
      EXPECT_EQ(true, (std::is_same<bool, true_::value_type>::value));
      EXPECT_EQ(true, true_::value);

      EXPECT_EQ(true, (std::is_same<int8_t, int8_<0>::value_type>::value));
      EXPECT_EQ(42, int8_<42>::value);
      EXPECT_EQ(true, (std::is_same<uint8_t, uint8_<0>::value_type>::value));
      EXPECT_EQ(42u, uint8_<42u>::value);
      EXPECT_EQ(true, (std::is_same<int16_t, int16_<0>::value_type>::value));
      EXPECT_EQ(42, int16_<42>::value);
      EXPECT_EQ(true, (std::is_same<uint16_t, uint16_<0>::value_type>::value));
      EXPECT_EQ(42u, uint16_<42u>::value);
      EXPECT_EQ(true, (std::is_same<int32_t, int32_<0>::value_type>::value));
      EXPECT_EQ(42, int32_<42>::value);
      EXPECT_EQ(true, (std::is_same<uint32_t, uint32_<0>::value_type>::value));
      EXPECT_EQ(42u, uint32_<42u>::value);

      EXPECT_EQ(true, (std::is_same<char, char_<0>::value_type>::value));
      EXPECT_EQ(42, char_<42>::value);
      EXPECT_EQ(true, (std::is_same<int, int_<0>::value_type>::value));
      EXPECT_EQ(42, int_<42>::value);
      EXPECT_EQ(true, (std::is_same<long, long_<0>::value_type>::value));
      EXPECT_EQ(42, long_<42>::value);

      EXPECT_EQ(true, (std::is_same<index_t, index_<0>::value_type>::value));
      EXPECT_EQ(42U, index_<42U>::value);
      EXPECT_EQ(true, (std::is_same<size_t, size_<0>::value_type>::value));
      EXPECT_EQ(42U, size_<42U>::value);

      EXPECT_EQ(sizeof(int), sizeof_<int>::value);
      EXPECT_EQ(alignof(int), alignof_<int>::value);
      EXPECT_EQ(static_cast<index_t>(-1), Npos::value);
   }

   /*
    * Test integral constant selection operations
    */
   TEST(TmetaBasic, Selection) {
      struct Foo {};
      struct Bar {};

      EXPECT_EQ (true, (std::is_same<int_<42>, if_c<true, int_<42>, false_>>()));
      EXPECT_EQ (true, (std::is_same<Foo,      if_c<false, int_<42>, Foo>>()));
      EXPECT_EQ (true, (std::is_same<Foo,      if_c<(bool)42, Foo, Bar>>()));

      EXPECT_EQ (true, (std::is_same<int_<42>, if_<true_, int_<42>, Bar>>()));
      EXPECT_EQ (true, (std::is_same<Bar,      if_<false_, int_<42>, Bar>>()));
      EXPECT_EQ (true, (std::is_same<int_<42>, if_<int_<1>, int_<42>, Bar>>()));
      EXPECT_EQ (true, (std::is_same<Foo,      if_<int_<0>, int_<42>, Foo>>()));

      EXPECT_EQ (true, (std::is_same<true_,  first_of<true_, false_>>()));
      EXPECT_EQ (false,(std::is_same<true_,  first_of<false_, true_>>()));
      EXPECT_EQ (false,(std::is_same<true_,  second_of<true_, false_>>()));
      EXPECT_EQ (true, (std::is_same<true_,  second_of<false_, true_>>()));

   }

   TEST(TmetaBasic, LogicalOperations) {
      struct Foo {};
      struct Bar {};

      EXPECT_EQ (true, (std::is_same<true_,  not_c<false>>::value));
      EXPECT_EQ (true, (std::is_same<false_, not_c<true>>::value));
      EXPECT_EQ (true, (std::is_same<false_, not_c<1>>::value));
      EXPECT_EQ (true, (std::is_same<true_,  not_c<0>>::value));

      EXPECT_EQ (true, (std::is_same<true_,  not_<false_>>()));
      EXPECT_EQ (true, (std::is_same<false_, not_<true_>>()));
      EXPECT_EQ (true, (std::is_same<true_,  not_<int_<0>>>()));
      EXPECT_EQ (true, (std::is_same<false_, not_<int_<1>>>()));

      EXPECT_EQ (true, (std::is_same<false_, or_<false_, false_, not_c<true>, int_<0>, not_<true_>>>()));
      EXPECT_EQ (true, (std::is_same<false_, or_<>>()));
      EXPECT_EQ (true, (std::is_same<int_<1>,or_<int_<1>>>()));
      EXPECT_EQ (true, (std::is_same<true_,  or_<true_>>()));
      EXPECT_EQ (true, (std::is_same<true_,  or_<false_, true_>>()));
      EXPECT_EQ (true, (std::is_same<true_,  or_<false_, false_, true_>>()));
      EXPECT_EQ (true, (std::is_same<int_<1>,or_<int_<0>, false_, not_<true_>, not_c<true>, int_<1>>>()));

      EXPECT_EQ (true, (std::is_same<true_,  and_<true_, true_, int_<1>, not_<false_>, not_c<false>>>()));
      EXPECT_EQ (true, (std::is_same<true_,  and_<>>()));
      EXPECT_EQ (true, (std::is_same<true_,  and_<true_>>()));
      EXPECT_EQ (true, (std::is_same<false_, and_<false_>>()));
      EXPECT_EQ (true, (std::is_same<true_,  and_<true_, true_>>()));
      EXPECT_EQ (true, (std::is_same<false_, and_<true_, false_>>()));
      EXPECT_EQ (true, (std::is_same<true_,  and_<true_, true_, true_>>()));
      EXPECT_EQ (true, (std::is_same<false_, and_<true_, true_, false_>>()));

      EXPECT_EQ (true,  (same_<Foo, Foo>()));
      EXPECT_EQ (false, (same_<Foo, Bar>()));
      EXPECT_EQ (true,  (not_same_<Foo, Bar>()));

   }

   /*
    * Test integral constant arithmetic operations
    */
   TEST(TmetaBasic, ArithmeticOperations) {
      EXPECT_EQ (int_<42>(), inc<int_<41>>());
      EXPECT_EQ (int_<42>(), dec<int_<43>>());
      EXPECT_EQ (int_<42>(), (add<int_<23>, add<int_<17>, int_<2>>>()));
      EXPECT_EQ (int_<42>(), (sub<int_<108>, int_<66>>()));
      EXPECT_EQ (int_<42>(), (mult<int_<7>, mult<int_<3>, int_<2>>>()));
      EXPECT_EQ (int_<42>(), (divide<int_<210>, int_<5>>()));
      EXPECT_EQ (int_<42>(), negate<int_<-42>>());
      EXPECT_EQ (int_< 1>(), (modulo<int_<43>, int_<42>>()));
   }

   /*
    * Test integral constant comparison operations
    */
   TEST(TmetaBasic, ComparisonOperations) {
      EXPECT_EQ (true,  (comp_eq<int_<7>, int_<7>>()));
      EXPECT_EQ (true,  (comp_eq<int_<-7>, int_<-7>>()));
      EXPECT_EQ (false, (comp_eq<int_<42>, int_<7>>()));

      EXPECT_EQ (true,  (comp_ne<int_<42>, int_<7>>()));
      EXPECT_EQ (false, (comp_ne<int_<42>, int_<42>>()));
      EXPECT_EQ (true,  (comp_ne<int_<42>, int_<-42>>()));

      EXPECT_EQ (true,  (comp_lt<int_<42>, int_<43>>()));
      EXPECT_EQ (true,  (comp_lt<int_<-7>, int_<-5>>()));
      EXPECT_EQ (false, (comp_lt<int_<43>, int_<42>>()));

      EXPECT_EQ (true,  (comp_gt<int_<42>, int_<7>>()));
      EXPECT_EQ (true,  (comp_gt<int_<7>,  int_<-42>>()));
      EXPECT_EQ (false, (comp_gt<int_<7>,  int_<42>>()));

      EXPECT_EQ (true,  (comp_le<int_<7>,  int_<42>>()));
      EXPECT_EQ (true,  (comp_le<int_<-7>, int_<42>>()));
      EXPECT_EQ (true,  (comp_le<int_<42>, int_<42>>()));
      EXPECT_EQ (true,  (comp_le<int_<-7>, int_<-7>>()));
      EXPECT_EQ (false, (comp_le<int_<42>, int_<7>>()));
      EXPECT_EQ (false, (comp_le<int_<7>,  int_<-42>>()));

      EXPECT_EQ (true,  (comp_ge<int_<42>, int_<7>>()));
      EXPECT_EQ (true,  (comp_ge<int_<42>, int_<-7>>()));
      EXPECT_EQ (true,  (comp_ge<int_<42>, int_<42>>()));
      EXPECT_EQ (true,  (comp_ge<int_<-7>, int_<-7>>()));
      EXPECT_EQ (false, (comp_ge<int_<42>, int_<43>>()));
      EXPECT_EQ (false, (comp_ge<int_<-7>, int_<42>>()));
   }

   /*
    * Test integral constant bit operations
    */
   TEST(TmetaBasic, BitOperations) {
      EXPECT_EQ (0x00, (bitand_<uint8_<0x55>, uint8_<0xAA>>()));
      EXPECT_EQ (0xFF, (bitor_ <uint8_<0x55>, uint8_<0xAA>>()));
      EXPECT_EQ (0xFA, (bitxor_<uint8_<0x55>, uint8_<0xAF>>()));
      EXPECT_EQ (0x00, (bitnot_<uint8_<(uint8_t)-1>>()));

      EXPECT_EQ (0x0000, (bitand_<uint16_<0x5555>, uint16_<0xAAAA>>()));
      EXPECT_EQ (0xFFFF, (bitor_ <uint16_<0x5555>, uint16_<0xAAAA>>()));
      EXPECT_EQ (0xFFAA, (bitxor_<uint16_<0x5555>, uint16_<0xAAFF>>()));
      EXPECT_EQ (0x0000, (bitnot_<uint16_<(uint16_t)-1>>()));

      EXPECT_EQ (0x04, (shift_left<uint8_<0x01>, uint8_<2>>()));
      EXPECT_EQ (0x00, (shift_left<uint8_<0x80>, uint8_<1>>()));
      EXPECT_EQ (0x02, (shift_right<uint8_<0x08>, uint8_<2>>()));
      EXPECT_EQ (0x00, (shift_right<uint8_<0x01>, uint8_<1>>()));
   }

   /*
    * SFINAE
    */
   template <typename T, typename =when<same_<T, int>::type::value>>
   int check1 (T x) { return x; }
   int check1 (...) { return 0; }

   template <typename T, typename =enable_if_t<same_<T, int>::type::value, void>>
   int check2 (T x) { return x; }
   int check2 (...) { return 0; }

   TEST(TmetaBasic, Sfinae) {
      EXPECT_EQ (42, check1(42));
      EXPECT_EQ (0,  check1(42.0));
      EXPECT_EQ (0,  check1());

      EXPECT_EQ (42, check2(42));
      EXPECT_EQ (0,  check2(42.0));
      EXPECT_EQ (0,  check2());
   }

}
