/*!
 * \file Tmeta.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 .
 *
 */
#include 
#include 
#include 
namespace test_meta {
   using namespace utl;
   using namespace meta;
   /*
    * Test integral constant
    */
   // Test type_of fixture
   template struct TestTypeOf {
      using type = T;
   };
   TEST(Tmeta_integral, Integreal_type_) {
      EXPECT_EQ(true, (std::is_same>>::value));
   }
   TEST(Tmeta_integral, IntegrealConstant) {
      EXPECT_EQ(true, (std::is_same::value_type>::value));
      EXPECT_EQ(true, (std::is_same::type::value_type>::value));
      EXPECT_EQ(42, (integral_constant::value_type(42)));
      EXPECT_EQ(42, (integral_constant()));
   }
   TEST(Tmeta_integral, BasicTypes) {
      EXPECT_EQ(true, (std::is_same::value_type>::value));
      EXPECT_EQ(true, bool_::value);
      EXPECT_EQ(true, (std::is_same::value));
      EXPECT_EQ(false, false_::value);
      EXPECT_EQ(true, (std::is_same::value));
      EXPECT_EQ(true, true_::value);
      EXPECT_EQ(true, (std::is_same::value_type>::value));
      EXPECT_EQ(42, int_<42>::value);
      EXPECT_EQ(true, (std::is_same::value_type>::value));
      EXPECT_EQ(42U, index_t_<42U>::value);
      EXPECT_EQ(true, (std::is_same::value_type>::value));
      EXPECT_EQ(42U, size_t_<42U>::value);
      EXPECT_EQ(sizeof(int), sizeof_::value);
      EXPECT_EQ(alignof(int), alignof_::value);
      EXPECT_EQ(static_cast(-1), Npos::value);
   }
   /*
    * Test integral constant arithmetic operations
    */
   TEST(Tmeta_integral, ArithmeticOperations) {
      EXPECT_EQ (int_<42>(), inc>());
      EXPECT_EQ (int_<42>(), dec>());
      EXPECT_EQ (int_<42>(), (add, add, int_<2>>>()));
      EXPECT_EQ (int_<42>(), (sub, int_<66>>()));
      EXPECT_EQ (int_<42>(), (mult, mult, int_<2>>>()));
      EXPECT_EQ (int_<42>(), (divide, int_<5>>()));
      EXPECT_EQ (int_<42>(), negate>());
      EXPECT_EQ (int_< 1>(), (modulo, int_<42>>()));
   }
   /*
    * Test logical
    */
   TEST(Tmeta_logical, ComparisonOperations) {
      EXPECT_EQ (true, (std::is_same, not_c>::value));
      EXPECT_EQ (true, (comp_eq, int_<7>>()));
      EXPECT_EQ (true, (comp_ne, int_<7>>()));
      EXPECT_EQ (true, (comp_lt, int_<43>>()));
      EXPECT_EQ (true, (comp_gt, int_<42>>()));
      EXPECT_EQ (true, (comp_le, int_<42>>()));
      EXPECT_EQ (true, (comp_ge, int_<42>>()));
   }
   TEST(Tmeta_logical, BitOperations) {
      EXPECT_EQ (0x00, (bitand_, int_<0xAA>>()));
      EXPECT_EQ (0xFF, (bitor_, int_<0xAA>>()));
      EXPECT_EQ (0xFA, (bitxor_, int_<0xAF>>()));
      EXPECT_EQ (0x00, (bitnot_>()));
      //XXX: add shifts
   }
   TEST(Tmeta_logical, TypeOperations) {
      struct Foo {};
      struct Bar {};
      EXPECT_EQ (true, (std::is_same, not_>>()));
      EXPECT_EQ (true, (std::is_same, if_c, bool_>>()));
      EXPECT_EQ (true, (std::is_same, if_, int_<42>, bool_>>()));
      EXPECT_EQ (true, (std::is_same,  or_,  bool_>>()));
      EXPECT_EQ (true, (std::is_same, or_, bool_>>()));
      EXPECT_EQ (true, (std::is_same, and_,  bool_>>()));
      EXPECT_EQ (true, (std::is_same,  and_,  bool_>>()));
      EXPECT_EQ (true, (same_()));
      EXPECT_EQ (false, (same_()));
      EXPECT_EQ (true, (not_same_()));
   }
   /*
    * Test void
    */
   TEST(Tmeta_void, VoidType) {
      struct Foo {};
      struct Bar {};
      EXPECT_EQ(true, (std::is_same>()));
   }
   /*
    * Test invoke
    */
   template struct MetaFunction { using type = int; };
   template   struct MetaClass {using apply = int; };
   TEST(Tmeta_invoke, Invoke) {
      //EXPECT_EQ (true, (is_evaluable()));
   }
   /*
    * Test typelist
    */
   template struct F {};
   TEST(Tmeta_typelist, List_fold) {
      struct X1 {};
      struct X2 {};
      struct X3 {};
      struct X4 {};
      using Q = quote;
      EXPECT_EQ(true, (std::is_same, void, Q>, void>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F, X2>>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F, X2>, X3>>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F, X2>, X3>, X4>>()));
      //EXPECT_EQ(true, (std::is_same, void, Q>, void>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F>>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F>>>()));
      EXPECT_EQ(true, (std::is_same, void, Q>, F>>>>()));
   }
   TEST(Tmeta_typelist, ListOperations) {
      using l1 = typelist;
      using l2 = typelist<>;
      using l3 = typelist;
      using l4 = typelist;
      using conc = typelist;
      using rep = typelist;
      EXPECT_EQ(3, size());
      EXPECT_EQ(true, empty());
      EXPECT_EQ(true, (std::is_same>()));
      EXPECT_EQ(true, (std::is_same, int>>()));
      EXPECT_EQ(true, (std::is_same>()));
      EXPECT_EQ(true, (std::is_same>()));
      EXPECT_EQ(true, (std::is_same>>()));
      EXPECT_EQ(true, (std::is_same, pop_front>()));
   }
}