/*!
 * \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 <http://www.gnu.org/licenses/>.
 *
 */
#include <utl/concepts/concepts.h>
#include <utl/meta/meta.h>
#include <gtest/gtest.h>


namespace test_concepts {
   using namespace utl;

   /*
    * Fixture like types
    */
   struct Empty { };
   struct HaveOnlyCopy {
      HaveOnlyCopy(const HaveOnlyCopy&)  = default;
      HaveOnlyCopy(HaveOnlyCopy&&)       = delete;
      HaveOnlyCopy& operator= (const HaveOnlyCopy&)  = default;
      HaveOnlyCopy& operator= (HaveOnlyCopy&&)       = delete;
   };
   struct HaveOnlyMove {
      HaveOnlyMove(const HaveOnlyMove&) = delete;
      HaveOnlyMove(HaveOnlyMove&&)      = default;
      HaveOnlyMove& operator= (const HaveOnlyMove&)  = delete;
      HaveOnlyMove& operator= (HaveOnlyMove&&)       = default;
   };
   struct HaveCopyAndMove {
      HaveCopyAndMove(const HaveCopyAndMove&) = default;
      HaveCopyAndMove(HaveCopyAndMove&&)      = default;
      HaveCopyAndMove& operator= (const HaveCopyAndMove&)  = default;
      HaveCopyAndMove& operator= (HaveCopyAndMove&&)       = default;
   };
   class HavePerfectForwarding {
      public: template<class T> HavePerfectForwarding(T&&) { }
   };
   class Base { };
   class Derived1 : public Base { };
   class Derived2 : public Derived1 { };
   class HaveOperatorBase {
      public: operator Base() { return base; }
      Base base;
   };


   struct A {
      int a_;
   public:
      A(int a =0) : a_{a} { };
      A(const A&) = default;
      A(A&&) = default;
   };
   bool operator== (const A& l, const A& r) { return l.a_ == r.a_; }
   bool operator!= (const A& l, const A& r) { return l.a_ != r.a_; }

   struct B {
      int b_;
   public:
      B(int b =0) : b_{b} { };
      B(const B&) = default;
      B(B&&)      = default;
      B& operator= (const B&) = default;
      B& operator= (B&&)      = default;
   };
   bool operator== (const B& l, const B& r) { return l.b_ == r.b_; }
   bool operator!= (const B& l, const B& r) { return l.b_ != r.b_; }
   bool operator<  (const B& l, const B& r) { return l.b_ <  r.b_; }
   bool operator<= (const B& l, const B& r) { return l.b_ <= r.b_; }
   bool operator>  (const B& l, const B& r) { return l.b_ >  r.b_; }
   bool operator>= (const B& l, const B& r) { return l.b_ >= r.b_; }


   TEST(TConcepts, Same) {
      // Same
      EXPECT_EQ (true,  (Same<int, int>));
      EXPECT_EQ (false, (Same<int, long>));
      EXPECT_EQ (true,  (Same<int*, int*>));
      EXPECT_EQ (true,  (Same<double&, double&>));
      EXPECT_EQ (false, (Same<int, Empty>));
      EXPECT_EQ (false, (Same<Base, Derived1>));
   }

   TEST(TConcepts, DerivedFrom) {
      // DerivedFrom
      EXPECT_EQ (true,  (DerivedFrom<Derived1, Base>));
      EXPECT_EQ (true,  (DerivedFrom<Derived2, Derived1>));
      EXPECT_EQ (true,  (DerivedFrom<Derived2, Base>));
      EXPECT_EQ (false, (DerivedFrom<Base, Derived1>));
      EXPECT_EQ (false, (DerivedFrom<Base, int>));
      EXPECT_EQ (false, (DerivedFrom<void, int>));
   }

   TEST(TConcepts, ConvertibleTo) {
      // ConvertibleTo
      EXPECT_EQ (true,  (ConvertibleTo<void, void>));
      EXPECT_EQ (false, (ConvertibleTo<Base, void>));
      EXPECT_EQ (false, (ConvertibleTo<Base*, Derived1*>));
      EXPECT_EQ (true,  (ConvertibleTo<Derived1*, Base*>));
      EXPECT_EQ (true,  (ConvertibleTo<HaveOperatorBase, Base>));
      EXPECT_EQ (true,  (ConvertibleTo<Base, HavePerfectForwarding>));
   }

   TEST(TConcepts, CommonReference) {
      // CommonReference
      EXPECT_EQ (true,  (CommonReference<Derived1, Base>));
      EXPECT_EQ (true,  (CommonReference<Derived1&, Base>));
      EXPECT_EQ (true,  (CommonReference<const Empty&&, const Empty&>));
      EXPECT_EQ (false, (CommonReference<Empty&, const volatile Empty&>));
      //FIXME: CommonReference needs SFINAE friendly implementation
      //EXPECT_EQ (false, (CommonReference<Empty&&, const volatile Empty&>)); <- yields compiler error

      // Common
      EXPECT_EQ (true,  (Common<int, int>));
      EXPECT_EQ (true,  (Common<Base, Derived1>));
      EXPECT_EQ (true,  (Common<Derived1, Derived2>));
      EXPECT_EQ (true,  (Common<Base, HaveOperatorBase>));
      EXPECT_EQ (true,  (Common<Base, HavePerfectForwarding>));
   }

   TEST(TConcepts, Integral) {
      // Integral
      EXPECT_EQ (false, (Integral<void>));
      EXPECT_EQ (true,  (Integral<int>));
      EXPECT_EQ (true,  (Integral<bool>));
      EXPECT_EQ (false, (Integral<int*>));
      EXPECT_EQ (false, (Integral<Base>));
      EXPECT_EQ (true,  (Integral<meta::int_<42>::value_type>));
      EXPECT_EQ (false, (Integral<meta::int_<42>::type>));

      // SignedIntegral
      EXPECT_EQ (false, (SignedIntegral<void>));
      EXPECT_EQ (true,  (SignedIntegral<int>));
      EXPECT_EQ (false, (SignedIntegral<int*>));
      EXPECT_EQ (false, (SignedIntegral<unsigned long>));
      EXPECT_EQ (false, (SignedIntegral<double>));
      EXPECT_EQ (false, (SignedIntegral<Base>));
      EXPECT_EQ (true,  (SignedIntegral<meta::int16_<42>::value_type>));

      // UnsignedIntegral
      EXPECT_EQ (false, (UnsignedIntegral<void>));
      EXPECT_EQ (true,  (UnsignedIntegral<unsigned int>));
      EXPECT_EQ (false, (UnsignedIntegral<long>));
      EXPECT_EQ (false, (UnsignedIntegral<double>));
      EXPECT_EQ (false, (UnsignedIntegral<Base>));
      EXPECT_EQ (true,  (UnsignedIntegral<meta::uint16_<42>::value_type>));
   }

   TEST(TConcepts, Assignable) {
      // MoveAssignable
      EXPECT_EQ (false, (MoveAssignable<void>));
      EXPECT_EQ (true,  (MoveAssignable<void*>));
      EXPECT_EQ (true,  (MoveAssignable<int>));
      EXPECT_EQ (true,  (MoveAssignable<int*>));
      EXPECT_EQ (false, (MoveAssignable<HaveOnlyCopy>));
      EXPECT_EQ (true,  (MoveAssignable<HaveOnlyMove>));
      EXPECT_EQ (true,  (MoveAssignable<HaveCopyAndMove>));
      EXPECT_EQ (true,  (MoveAssignable<Empty>));
      EXPECT_EQ (true,  (MoveAssignable<HavePerfectForwarding>));

      // CopyAssignable
      EXPECT_EQ (false, (CopyAssignable<void>));
      EXPECT_EQ (true,  (CopyAssignable<void*>));
      EXPECT_EQ (true,  (CopyAssignable<int>));
      EXPECT_EQ (true,  (CopyAssignable<int*>));
      EXPECT_EQ (true,  (CopyAssignable<HaveOnlyCopy>));
      EXPECT_EQ (false, (CopyAssignable<HaveOnlyMove>));
      EXPECT_EQ (true,  (CopyAssignable<HaveCopyAndMove>));
      EXPECT_EQ (true,  (CopyAssignable<Empty>));
      EXPECT_EQ (true,  (CopyAssignable<HavePerfectForwarding>));

      // Assignable
      EXPECT_EQ (false, (Assignable<void, void>));
      EXPECT_EQ (false, (Assignable<int&, void>));
      EXPECT_EQ (true,  (Assignable<int&, int>));
      EXPECT_EQ (false, (Assignable<int, int>));
      EXPECT_EQ (false, (Assignable<int*, int*>));
      EXPECT_EQ (true,  (Assignable<Base&, Derived1>));
      EXPECT_EQ (false, (Assignable<Derived1&, Base>));
      EXPECT_EQ (true,  (Assignable<HaveOnlyMove&, HaveOnlyMove&&>));
      EXPECT_EQ (true , (Assignable<HaveOnlyMove&, HaveOnlyMove>));
      EXPECT_EQ (true,  (Assignable<Empty&, Empty>));
   }

   TEST(TConcepts, Swappable) {
      // Swappable, SwappableWith
      EXPECT_EQ (false, (Swappable<void>));
      EXPECT_EQ (true,  (Swappable<void*>));
      EXPECT_EQ (true,  (Swappable<int>));
      EXPECT_EQ (true,  (Swappable<Base>));
      EXPECT_EQ (true,  (SwappableWith<int, int>));
      EXPECT_EQ (false, (SwappableWith<int, Base>));
      EXPECT_EQ (false, (SwappableWith<Base, Derived1>));

      // Destructible
      EXPECT_EQ (false, (Destructible<void>));
      EXPECT_EQ (true,  (Destructible<void*>));
      EXPECT_EQ (true,  (Destructible<int>));
      EXPECT_EQ (true,  (Destructible<int&>));
      EXPECT_EQ (true,  (Destructible<Base>));
      EXPECT_EQ (true,  (Destructible<HavePerfectForwarding>));
   }

   TEST(TConcepts, Constructible) {
      // Constructible
      EXPECT_EQ (false, (Constructible<void>));
      EXPECT_EQ (true,  (Constructible<void*>));
      EXPECT_EQ (true,  (Constructible<Base>));
      EXPECT_EQ (false, (Constructible<HaveOnlyMove>));
      EXPECT_EQ (true,  (Constructible<HavePerfectForwarding, int>));

      // DefaultConstructible
      EXPECT_EQ (false, (DefaultConstructible<void>));
      EXPECT_EQ (true,  (DefaultConstructible<void*>));
      EXPECT_EQ (false, (DefaultConstructible<int&>));
      EXPECT_EQ (true,  (DefaultConstructible<Base>));
      EXPECT_EQ (true,  (DefaultConstructible<Derived1>));
      EXPECT_EQ (false, (DefaultConstructible<HaveOnlyCopy>));
      EXPECT_EQ (false, (DefaultConstructible<HaveOnlyMove>));
      EXPECT_EQ (false, (DefaultConstructible<HavePerfectForwarding>));

      // MoveConstructible
      EXPECT_EQ (false, (MoveConstructible<void>));
      EXPECT_EQ (true,  (MoveConstructible<void*>));
      EXPECT_EQ (true,  (MoveConstructible<Base>));
      EXPECT_EQ (true,  (MoveConstructible<Derived1>));
      EXPECT_EQ (true,  (MoveConstructible<HaveOnlyMove>));
      EXPECT_EQ (false, (MoveConstructible<HaveOnlyCopy>));
      EXPECT_EQ (true,  (MoveConstructible<HaveCopyAndMove>));
      EXPECT_EQ (true , (MoveConstructible<HavePerfectForwarding>));

      // CopyConstructible
      EXPECT_EQ (false, (CopyConstructible<void>));
      EXPECT_EQ (true,  (CopyConstructible<void*>));
      EXPECT_EQ (true,  (CopyConstructible<Base>));
      EXPECT_EQ (true,  (CopyConstructible<Derived1>));
      EXPECT_EQ (false, (CopyConstructible<HaveOnlyMove>));
      EXPECT_EQ (false, (CopyConstructible<HaveOnlyCopy>));
      EXPECT_EQ (true,  (CopyConstructible<HaveCopyAndMove>));
      EXPECT_EQ (true , (CopyConstructible<HavePerfectForwarding>));
   }

   TEST(TConcepts, MovableCopyable) {
      // Movable
      EXPECT_EQ (false, (Movable<void>));
      EXPECT_EQ (true,  (Movable<int>));
      EXPECT_EQ (true,  (Movable<Base>));
      EXPECT_EQ (true,  (Movable<Derived1>));
      EXPECT_EQ (true,  (Movable<HaveOnlyMove>));
      EXPECT_EQ (false, (Movable<HaveOnlyCopy>));
      EXPECT_EQ (true,  (Movable<HaveCopyAndMove>));
      EXPECT_EQ (true , (Movable<HavePerfectForwarding>));

      // Copyable
      EXPECT_EQ (false, (Copyable<void>));
      EXPECT_EQ (true,  (Copyable<int>));
      EXPECT_EQ (true,  (Copyable<Base>));
      EXPECT_EQ (true,  (Copyable<Derived1>));
      EXPECT_EQ (false, (Copyable<HaveOnlyMove>));
      EXPECT_EQ (false, (Copyable<HaveOnlyCopy>));
      EXPECT_EQ (true,  (Copyable<HaveCopyAndMove>));
      EXPECT_EQ (true , (Copyable<HavePerfectForwarding>));
   }

   TEST(TConcepts, Boolean) {
      // Boolean
      EXPECT_EQ (false, (Boolean<void>));
      EXPECT_EQ (true,  (Boolean<bool>));
      EXPECT_EQ (true,  (Boolean<int>));
      EXPECT_EQ (true,  (Boolean<double>));
      EXPECT_EQ (true,  (Boolean<std::true_type>));
      EXPECT_EQ (true,  (Boolean<meta::true_>));
      EXPECT_EQ (false, (Boolean<Empty>));
   }

   TEST(TConcepts, Comparable) {
      // EqualityComparable
      EXPECT_EQ (false, (EqualityComparable<void>));
      EXPECT_EQ (true,  (EqualityComparable<bool>));
      EXPECT_EQ (true,  (EqualityComparable<int>));
      EXPECT_EQ (false, (EqualityComparable<Empty>));
      EXPECT_EQ (true,  (EqualityComparable<A>));

      // EqualityComparableWith
      EXPECT_EQ (false, (EqualityComparableWith<void, bool>));
      EXPECT_EQ (false, (EqualityComparableWith<void, void>));
      EXPECT_EQ (true,  (EqualityComparableWith<bool, bool>));
      EXPECT_EQ (true,  (EqualityComparableWith<int, int>));
      EXPECT_EQ (true,  (EqualityComparableWith<int, bool>));
      EXPECT_EQ (false, (EqualityComparableWith<Empty, Empty>));
      EXPECT_EQ (false, (EqualityComparableWith<int, Empty>));
      EXPECT_EQ (true,  (EqualityComparableWith<A, A>));
      EXPECT_EQ (false, (EqualityComparableWith<A, B>));

      // StrictTotallyOrdered
      EXPECT_EQ (false, (StrictTotallyOrdered<void>));
      EXPECT_EQ (true,  (StrictTotallyOrdered<bool>));
      EXPECT_EQ (true,  (StrictTotallyOrdered<int>));
      EXPECT_EQ (true,  (StrictTotallyOrdered<double>));
      EXPECT_EQ (false, (StrictTotallyOrdered<Empty>));
      EXPECT_EQ (false, (StrictTotallyOrdered<A>));
      EXPECT_EQ (true,  (StrictTotallyOrdered<B>));

      // StrictTotallyOrderedWith
      EXPECT_EQ (false, (StrictTotallyOrderedWith<void, void>));
      EXPECT_EQ (false, (StrictTotallyOrderedWith<int, void>));
      EXPECT_EQ (true,  (StrictTotallyOrderedWith<bool, bool>));
      EXPECT_EQ (true,  (StrictTotallyOrderedWith<int, double>));
      EXPECT_EQ (false, (StrictTotallyOrderedWith<int, Empty>));
      EXPECT_EQ (false, (StrictTotallyOrderedWith<Base, Derived1>));
      EXPECT_EQ (false, (StrictTotallyOrderedWith<A, A>));
      EXPECT_EQ (true,  (StrictTotallyOrderedWith<B, B>));
      EXPECT_EQ (false, (StrictTotallyOrderedWith<A, B>));
   }

   TEST(TConcepts, Types) {
      // Semiregular
      EXPECT_EQ (false, (Semiregular<void>));
      EXPECT_EQ (true,  (Semiregular<int>));
      EXPECT_EQ (true,  (Semiregular<Empty>));
      EXPECT_EQ (false, (Semiregular<HaveOnlyMove>));
      EXPECT_EQ (false, (Semiregular<HaveOnlyCopy>));
      EXPECT_EQ (false, (Semiregular<HaveCopyAndMove>));
      EXPECT_EQ (false, (Semiregular<A>));
      EXPECT_EQ (true,  (Semiregular<B>));

      // Regular
      EXPECT_EQ (false, (Regular<void>));
      EXPECT_EQ (true,  (Regular<int>));
      EXPECT_EQ (true,  (Regular<int*>));
      EXPECT_EQ (false, (Regular<Empty>));
      EXPECT_EQ (false, (Regular<HaveOnlyMove>));
      EXPECT_EQ (false, (Regular<HaveOnlyCopy>));
      EXPECT_EQ (false, (Regular<HaveCopyAndMove>));
      EXPECT_EQ (false, (Regular<A>));
      EXPECT_EQ (true,  (Regular<B>));

      // Scalar
      EXPECT_EQ (false, (Scalar<void>));
      EXPECT_EQ (true,  (Scalar<int>));
      EXPECT_EQ (true,  (Scalar<long*>));
      EXPECT_EQ (false, (Scalar<A>));
      EXPECT_EQ (false, (Scalar<B>));

      // Arithmetic
      EXPECT_EQ (false, (Arithmetic<void>));
      EXPECT_EQ (true,  (Arithmetic<int>));
      EXPECT_EQ (false, (Arithmetic<long*>));
      EXPECT_EQ (false, (Arithmetic<A>));
      EXPECT_EQ (false, (Arithmetic<B>));

      // FloatingPoint
      EXPECT_EQ (false, (FloatingPoint<void>));
      EXPECT_EQ (true,  (FloatingPoint<float>));
      EXPECT_EQ (true,  (FloatingPoint<double>));
      EXPECT_EQ (false, (FloatingPoint<int>));
      EXPECT_EQ (false, (FloatingPoint<float*>));
      EXPECT_EQ (false, (FloatingPoint<A>));
      EXPECT_EQ (false, (FloatingPoint<B>));
   }

   struct Inv {
      void operator() (int) { };
      void operator() () { };
   };
   struct Pred {
      bool operator () (int) { return true; }
      bool operator () (int, int) { return true; }
   };
   TEST(TConcepts, Callable) {
      EXPECT_EQ (true,  (Invocable<Inv, int>));
      EXPECT_EQ (true,  (Invocable<Inv>));
      EXPECT_EQ (true,  (Invocable<Inv, double>));
      EXPECT_EQ (false, (Invocable<Inv, Empty>));

      EXPECT_EQ (true,  (RegularInvocable<Inv, int>));

      EXPECT_EQ (false, (Predicate<Inv, int>));
      EXPECT_EQ (true,  (Predicate<Pred, int>));
      EXPECT_EQ (false, (Predicate<Pred, Empty>));

      EXPECT_EQ (true,  (Relation<Pred, int, int>));
      EXPECT_EQ (true,  (Relation<Pred, int, double>));
      EXPECT_EQ (false, (Relation<Pred, Empty, int>));

      EXPECT_EQ (true,  (StrictWeakOrder<Pred, int, int>));
      EXPECT_EQ (false, (StrictWeakOrder<Pred, int, Empty>));
   }

   struct Incr {
      Incr& operator++() { return *this; }
      Incr operator++(int) { return *this; }
   };
   int type_printer (int* i) { return *i; }
   TEST(TConcepts, Iterators) {

//      type_printer(detail::try_ppI<Incr&>{});
//      type_printer(detail::try_Ipp<Incr&>{});
//      type_printer(meta::detected_t<detail::try_ppI, int>{});
      EXPECT_EQ (true,  (WeaklyIncrementable<int>));
      EXPECT_EQ (false, (WeaklyIncrementable<void>));
      EXPECT_EQ (false, (WeaklyIncrementable<meta::nil_>));
      EXPECT_EQ (true,  (WeaklyIncrementable<Incr>));
      EXPECT_EQ (false, (WeaklyIncrementable<Incr&>));
   }
}

